Compare commits
2279 Commits
android-v1
...
android-v1
Author | SHA1 | Date | |
---|---|---|---|
|
86bfdb5c79 | ||
|
fc7d34f79c | ||
|
9d962f0328 | ||
|
79e1a33b28 | ||
|
0eedae1f62 | ||
|
e1144c098e | ||
|
5c5cb0f781 | ||
|
656615b571 | ||
|
799a9e810d | ||
|
aa147bbcdc | ||
|
8c7a24282c | ||
|
f99f3f8a6d | ||
|
29fbafdfff | ||
|
3253146dae | ||
|
7e4ac0fd73 | ||
|
60d77cb5ea | ||
|
8299164964 | ||
|
488393e75d | ||
|
c19f8c8035 | ||
|
b0b55718cc | ||
|
5ade9ff2f6 | ||
|
e4cfb518ba | ||
|
89864de1ff | ||
|
ee2a474611 | ||
|
223caca0f5 | ||
|
ee358f70dd | ||
|
0718828d60 | ||
|
d13f54c2ce | ||
|
90f7f99cd1 | ||
|
a273bbaeee | ||
|
619b426689 | ||
|
97c752a39e | ||
|
7565f1cada | ||
|
0c147236a3 | ||
|
88f22fabf7 | ||
|
5f46d60c5e | ||
|
cc8c200826 | ||
|
4485947b0f | ||
|
9f1a877f96 | ||
|
13280ce1b3 | ||
|
7f73931530 | ||
|
bbfed9bca8 | ||
|
bab29cd582 | ||
|
baea44cbd6 | ||
|
ab0538df43 | ||
|
58d4a69053 | ||
|
9147b3061a | ||
|
1b0102f62c | ||
|
471631933b | ||
|
cd761932c1 | ||
|
e63eee89ef | ||
|
4393ebbcc6 | ||
|
ed82390a8d | ||
|
79e708779f | ||
|
98905f6892 | ||
|
44e57c3959 | ||
|
35d48394ce | ||
|
eb42a5f34b | ||
|
729c8c6fac | ||
|
7a1707d864 | ||
|
64d7603eed | ||
|
89e6b680a6 | ||
|
da071a804c | ||
|
7833ca5ae6 | ||
|
44d49b57b9 | ||
|
9db150f8ff | ||
|
e2d3630783 | ||
|
cb1b5cae9f | ||
|
3af5b31c1c | ||
|
b6dafd6da6 | ||
|
d08b922632 | ||
|
9d4e250f6b | ||
|
c19cc1b39a | ||
|
73bd6f9776 | ||
|
6fef55398f | ||
|
d209d5036b | ||
|
4be02bc33c | ||
|
d6daa34e0a | ||
|
9cebbbe7cf | ||
|
71e5304298 | ||
|
8f8d11c9b3 | ||
|
1cd86fd3ea | ||
|
7f1f5a8c3d | ||
|
9dfb0642da | ||
|
0fa8dfa063 | ||
|
1d79dedf6d | ||
|
a274a56e65 | ||
|
44d3a4213f | ||
|
e68eb196b7 | ||
|
4bef79cd71 | ||
|
f3dc3602c8 | ||
|
17e140ba56 | ||
|
89d0575ccd | ||
|
ca0197a319 | ||
|
62bc296abe | ||
|
e58dc809ec | ||
|
abd57ad384 | ||
|
bada9286d0 | ||
|
3807317e66 | ||
|
10ff43f4f0 | ||
|
282f6de1a9 | ||
|
9a55afec01 | ||
|
c8c4bb3245 | ||
|
e1f831af62 | ||
|
657cebfda9 | ||
|
7f1c25793a | ||
|
b770ffda4d | ||
|
d0b3e15999 | ||
|
20433b0282 | ||
|
452b41de0f | ||
|
b69008225f | ||
|
b3a778e983 | ||
|
a57c6e9155 | ||
|
3fbfba2c03 | ||
|
8a5e6875f0 | ||
|
e11e57f1d8 | ||
|
073bd80f89 | ||
|
e3aaee738a | ||
|
f1b2b7b86b | ||
|
e0a87d6253 | ||
|
a7eae2e033 | ||
|
51235f191d | ||
|
003ead2511 | ||
|
8dc8527fdc | ||
|
985dcc2605 | ||
|
4063cdba11 | ||
|
c96c591fa9 | ||
|
4913fdb800 | ||
|
24bc7f153a | ||
|
8732c97dcd | ||
|
e092eaef6f | ||
|
4ade1bf65f | ||
|
77a03599de | ||
|
9c057071b5 | ||
|
ee3f4beeaf | ||
|
7035b1f9f6 | ||
|
3aea55ee63 | ||
|
28acb4eca6 | ||
|
c63c6370b5 | ||
|
6046f40e45 | ||
|
262159cf73 | ||
|
8eb94bc8ab | ||
|
a9390affaa | ||
|
66392b4e8a | ||
|
3283f34c79 | ||
|
fd4e5d7d30 | ||
|
5bbf9d87c2 | ||
|
acf1ff6d1d | ||
|
05acc51054 | ||
|
eb8841379c | ||
|
d601575549 | ||
|
0d2f253218 | ||
|
349d020da2 | ||
|
87ccb86f54 | ||
|
20482b2f14 | ||
|
47a05fb4ba | ||
|
ac0a89625a | ||
|
a07638239b | ||
|
5143870d3b | ||
|
f61c4c1374 | ||
|
254735fcae | ||
|
44024371ff | ||
|
be232fa34b | ||
|
1a90165255 | ||
|
13417d15b7 | ||
|
9fe1f6acfc | ||
|
1899d86641 | ||
|
6ca63ca56b | ||
|
e796968ec0 | ||
|
7b64c8dbe3 | ||
|
2377f7bc07 | ||
|
47fdcef182 | ||
|
d27b658392 | ||
|
07720ed6f8 | ||
|
a9cdecf599 | ||
|
c6b5a5c328 | ||
|
e7f3aa3764 | ||
|
89ca8e08d6 | ||
|
6350506ce7 | ||
|
d5ed611920 | ||
|
acc10ccac4 | ||
|
45160a2e73 | ||
|
e38794171a | ||
|
c2d6da83c0 | ||
|
fd568b5300 | ||
|
77005fc495 | ||
|
0273e58783 | ||
|
3e88a24753 | ||
|
1205908233 | ||
|
3a7a068196 | ||
|
845ecfe742 | ||
|
53355aaad3 | ||
|
7cd0e25538 | ||
|
2f15178ff6 | ||
|
a4b13be0d1 | ||
|
23ae4fb790 | ||
|
cff036d08b | ||
|
7826cc0b03 | ||
|
2bfa28a311 | ||
|
780c5c80ae | ||
|
fcd00b3212 | ||
|
6a41d6e85a | ||
|
3733858145 | ||
|
95d8ee65a5 | ||
|
8e57697b71 | ||
|
c9c371b297 | ||
|
d4f794920e | ||
|
13b5ef36d7 | ||
|
83e0bac52a | ||
|
9943ae0fa5 | ||
|
4c95872826 | ||
|
4aef72572d | ||
|
126918be54 | ||
|
1dc4516efc | ||
|
9a9cfbd130 | ||
|
341c9ba64b | ||
|
f432734338 | ||
|
b7f5f848f2 | ||
|
d80cf6bf37 | ||
|
31bce2e644 | ||
|
faabd17fc6 | ||
|
4c4ed60cfb | ||
|
be1895a080 | ||
|
b81b6135f6 | ||
|
583ac7960b | ||
|
b749bb60fc | ||
|
2c6a298758 | ||
|
42544cc63b | ||
|
d2b81d221b | ||
|
618d8723d1 | ||
|
8058d3f728 | ||
|
0bc10acf02 | ||
|
e5866e8aa6 | ||
|
b4e907ae77 | ||
|
ff23248017 | ||
|
ac0d01b724 | ||
|
40adbc5e9e | ||
|
f36e0c2513 | ||
|
5be8c2cdcf | ||
|
e915549f6d | ||
|
30b3f3190b | ||
|
3f7390d104 | ||
|
005bfe799c | ||
|
c9f1715899 | ||
|
faf5eac806 | ||
|
9c5ab41b05 | ||
|
a8c8539e7a | ||
|
a3153f1c9f | ||
|
f23ab37a11 | ||
|
334da21a3b | ||
|
4a7c1f54d2 | ||
|
643b06ffa7 | ||
|
0f4a781df0 | ||
|
d6cc84aabd | ||
|
33ad0dce15 | ||
|
949c92f6d6 | ||
|
48098b5c06 | ||
|
afcfb0e341 | ||
|
a1364a621a | ||
|
4113252f18 | ||
|
386d11f51a | ||
|
39a506ad52 | ||
|
d9c266e3f1 | ||
|
f4092c0824 | ||
|
4fd20b1c5a | ||
|
0d5586b379 | ||
|
da77de0a08 | ||
|
dd34b155b4 | ||
|
26ce102113 | ||
|
5082181c49 | ||
|
0ee370303c | ||
|
720494d870 | ||
|
cfe1911723 | ||
|
b2318b1f9f | ||
|
560db37025 | ||
|
dd557f66a5 | ||
|
0603f179cc | ||
|
21fcaf621d | ||
|
794c8dd051 | ||
|
f020b571da | ||
|
071ea5854f | ||
|
bfec890632 | ||
|
3bb9d2efd6 | ||
|
e117b6f732 | ||
|
710026c8ac | ||
|
79c45af0d5 | ||
|
a75db94da5 | ||
|
468261906a | ||
|
3f1c9c989b | ||
|
3b40de1962 | ||
|
2dda1c82c5 | ||
|
4bc4d41728 | ||
|
c8c160d912 | ||
|
99354f7dbe | ||
|
279310f9a5 | ||
|
d0de79bffa | ||
|
fbd42726c0 | ||
|
dfe02fbe96 | ||
|
a8cffb9ac8 | ||
|
bdbf2fe583 | ||
|
36776cd615 | ||
|
1852d9291d | ||
|
0e5a0fdbe5 | ||
|
7e59c40091 | ||
|
53eba3f062 | ||
|
334347abcf | ||
|
e43e3c198a | ||
|
a8b8da4732 | ||
|
ea57f5655c | ||
|
68496bd1a1 | ||
|
097d4d5735 | ||
|
89224c8f88 | ||
|
22c0a219aa | ||
|
9eed42cea0 | ||
|
be24cbed2d | ||
|
aef3e0aead | ||
|
7a06a41502 | ||
|
03948d185d | ||
|
40d39d6268 | ||
|
66f1506429 | ||
|
f19f148297 | ||
|
79c6eb5503 | ||
|
1204bed80a | ||
|
dc91b1f7d6 | ||
|
66dc2b33de | ||
|
c97c337c79 | ||
|
0f7deeeac1 | ||
|
8c338675d2 | ||
|
b126c761cd | ||
|
d02513bb70 | ||
|
68e8d69d04 | ||
|
4eb680d675 | ||
|
068dbfffd8 | ||
|
d9ec2efa4b | ||
|
1c90e9dd7d | ||
|
811942d45e | ||
|
dbde239a25 | ||
|
ab3ba3e2ca | ||
|
79e77b5a10 | ||
|
c3395133c9 | ||
|
b1d073cdeb | ||
|
2c115cd074 | ||
|
c99bd27e4d | ||
|
25d723b373 | ||
|
0cfceab151 | ||
|
65a0e056c7 | ||
|
b27a215c7f | ||
|
e9783d050b | ||
|
e1be1ead86 | ||
|
b129bed69f | ||
|
265ad50ce4 | ||
|
3e2570601f | ||
|
05513e67d2 | ||
|
34e4164e2f | ||
|
e4123b4ad9 | ||
|
ee6325e3c2 | ||
|
a2fce031a2 | ||
|
2de30f8b82 | ||
|
f1dbc1d41c | ||
|
a96734f5be | ||
|
b83eee751f | ||
|
49451dba20 | ||
|
3830f36c0b | ||
|
9f7eeb0510 | ||
|
58396ea5ce | ||
|
b1bb470b0a | ||
|
0b6f5581f0 | ||
|
c34f762507 | ||
|
9eac63c021 | ||
|
1fec7d5558 | ||
|
5eb084fd3b | ||
|
a99db36578 | ||
|
bec8a2e7d1 | ||
|
38ced8cb09 | ||
|
8eff3f5616 | ||
|
6f8332bad1 | ||
|
01e0c0c21f | ||
|
7a1eecc815 | ||
|
bec91863e2 | ||
|
341b763088 | ||
|
e0721493e9 | ||
|
2d467f7fa5 | ||
|
23ecbfcb3a | ||
|
f8ad782991 | ||
|
c2684a8149 | ||
|
4e7c6b0aea | ||
|
c568f5b33c | ||
|
e85d4fca5d | ||
|
d9601f3136 | ||
|
3ee5deb9f3 | ||
|
8bc92e88df | ||
|
ebe06c9c38 | ||
|
b3f32ffc59 | ||
|
ecc50790ed | ||
|
a09c7b72c7 | ||
|
7d767cf0c9 | ||
|
b5fc8e7fc1 | ||
|
91ec0dce37 | ||
|
1aa2786ba0 | ||
|
3265fafd76 | ||
|
17fd8ee504 | ||
|
05adb06aeb | ||
|
dacf6377ae | ||
|
fa76c5db8d | ||
|
17f6d6e8c1 | ||
|
347d84526b | ||
|
172327613f | ||
|
3e1ee7f330 | ||
|
22b5888671 | ||
|
41edae9719 | ||
|
6446a3c4de | ||
|
9c4939e051 | ||
|
9f9f760ede | ||
|
488cb99d4c | ||
|
4167ecafd2 | ||
|
6ca41ddf80 | ||
|
734f83470c | ||
|
0f8f7aeb14 | ||
|
3b653a95a0 | ||
|
06eb389b21 | ||
|
17d2d6f959 | ||
|
20e065a371 | ||
|
57cf826e2c | ||
|
dbe8d3a68e | ||
|
5a174b2236 | ||
|
b8158f3b53 | ||
|
e768b8cfea | ||
|
c593546575 | ||
|
578f93fb90 | ||
|
24a90036b7 | ||
|
488c0f1828 | ||
|
f4717d0a3d | ||
|
913c78f45d | ||
|
4e90c05472 | ||
|
d31d9f2638 | ||
|
0cf56d5a9a | ||
|
80112e7b35 | ||
|
b2f1fac133 | ||
|
cd57877175 | ||
|
30f515d473 | ||
|
199113341f | ||
|
64aef69d74 | ||
|
2073755498 | ||
|
f72b5c1028 | ||
|
561b24f6fd | ||
|
d41221bd90 | ||
|
132c6543b2 | ||
|
7152344fe1 | ||
|
a04092c99c | ||
|
c273fd4d63 | ||
|
e06dd9c69f | ||
|
463b20013d | ||
|
204fbec648 | ||
|
d65706aa9c | ||
|
f2a1597ecc | ||
|
9a2f0bee29 | ||
|
3f8833eaf1 | ||
|
c3a1e7c6e9 | ||
|
f5aec137bd | ||
|
20960dbd47 | ||
|
e45fd6b4d3 | ||
|
8eacba972e | ||
|
51732a5adb | ||
|
f51873d877 | ||
|
d738fa54ad | ||
|
ec8ccc94aa | ||
|
cb8dca747b | ||
|
b16ebbbf7a | ||
|
90ccb34916 | ||
|
724af8f757 | ||
|
125fa8647a | ||
|
bf47237d8f | ||
|
294b9fc941 | ||
|
699c1ab58c | ||
|
150ee14de6 | ||
|
b207f7a53f | ||
|
096698912e | ||
|
a5ec1be7d1 | ||
|
f9f884a7f3 | ||
|
31d59c1e21 | ||
|
7663044e9f | ||
|
7ceb68d835 | ||
|
ac2635a363 | ||
|
2496e6e738 | ||
|
b4b36c6f22 | ||
|
0171ea837f | ||
|
79a343c2d9 | ||
|
dd5ed48c7e | ||
|
e7169cc70d | ||
|
c65ee4a424 | ||
|
feee162578 | ||
|
d1cab4b7f5 | ||
|
d2acf314f5 | ||
|
b803984773 | ||
|
d2582f4fdf | ||
|
dfd18ebe24 | ||
|
94082bc6a5 | ||
|
5c3c72c37e | ||
|
d730e068b0 | ||
|
b2f8c38842 | ||
|
71b2e36429 | ||
|
e598c64241 | ||
|
2b2ec2c655 | ||
|
381d757525 | ||
|
d54c59cf74 | ||
|
1f481fba4e | ||
|
69cd88e4a5 | ||
|
b140a9d7de | ||
|
b1b530edf2 | ||
|
ab63316f83 | ||
|
35df8e5d9e | ||
|
ea3c82733c | ||
|
60f75fe2db | ||
|
9214095a64 | ||
|
676c43ebab | ||
|
e65af8c1e4 | ||
|
2321455723 | ||
|
9260235224 | ||
|
56801c0a08 | ||
|
aa738cc086 | ||
|
900b0f3eb2 | ||
|
b5a64e2c3e | ||
|
4163d9e474 | ||
|
d769dc2b94 | ||
|
c9fc52ec74 | ||
|
fe72568d3b | ||
|
c5c379f38a | ||
|
2050889590 | ||
|
6164e2d8eb | ||
|
30c175ef29 | ||
|
f6fed72b64 | ||
|
7596ff2eda | ||
|
142976f012 | ||
|
ab5c97f75a | ||
|
aea3de982a | ||
|
83aff6f478 | ||
|
6cb7154bf4 | ||
|
3aa38d35d7 | ||
|
870a76c570 | ||
|
206a64eff6 | ||
|
6b97839617 | ||
|
456d979aa6 | ||
|
ea5e0d337c | ||
|
9651317695 | ||
|
a4cc2076d7 | ||
|
db820d500d | ||
|
722192127c | ||
|
36e23d6432 | ||
|
8cfb014f60 | ||
|
b4a572c8ae | ||
|
531e92e2da | ||
|
53a857b706 | ||
|
4f5473f8a2 | ||
|
1b617a4adc | ||
|
7741a43d42 | ||
|
776411f882 | ||
|
ae4cecc621 | ||
|
5be98a46e3 | ||
|
1907ef0c99 | ||
|
bd99b25848 | ||
|
616401ebba | ||
|
d0fb6acccd | ||
|
12a7b3c73c | ||
|
75b28c46af | ||
|
93dccf62df | ||
|
ec499eecd5 | ||
|
7ccd19e21d | ||
|
a6459d3641 | ||
|
9b26378fdd | ||
|
6b8e84332d | ||
|
6d56bb8afd | ||
|
be9e873277 | ||
|
10feeeeb6b | ||
|
518af9dc0a | ||
|
093fc811eb | ||
|
2a8b27033f | ||
|
62bddd7c69 | ||
|
113f91b3c5 | ||
|
d40ad2de89 | ||
|
3b7733018c | ||
|
e319864669 | ||
|
7251813634 | ||
|
e024015d5e | ||
|
3f5e6d72d6 | ||
|
9fd43a30f9 | ||
|
0707a0e05f | ||
|
f550d847c4 | ||
|
1e9a0036e7 | ||
|
16cf0a3058 | ||
|
0fa19a281c | ||
|
9c53f485e6 | ||
|
dc131f77d4 | ||
|
712601b8c0 | ||
|
ff7775b344 | ||
|
0512fa6208 | ||
|
27ab2b8e30 | ||
|
9fa7c9c20a | ||
|
6bd0250ef8 | ||
|
0d736bcb58 | ||
|
c1129604ba | ||
|
693f6cbfe7 | ||
|
7f397a4da8 | ||
|
52ba5c5bb7 | ||
|
ce24942a03 | ||
|
719afba05a | ||
|
c99dd2ba87 | ||
|
a59cc94afc | ||
|
336cadbc12 | ||
|
a946dc69c1 | ||
|
e2e95df057 | ||
|
e095369e1a | ||
|
1c938a5998 | ||
|
a45128807e | ||
|
d54e52b1a8 | ||
|
5743729d11 | ||
|
11d8466db1 | ||
|
c3360d6c48 | ||
|
37158fdb89 | ||
|
ae73051797 | ||
|
62db3d09ea | ||
|
f82aa0adde | ||
|
5e5b6cdc42 | ||
|
351306eb03 | ||
|
e4fffa52d4 | ||
|
d622ff4a78 | ||
|
b6d4fd16c9 | ||
|
a548d695f2 | ||
|
499f318192 | ||
|
42ecf98344 | ||
|
6f08e1e4ff | ||
|
9524eb7e37 | ||
|
84e478f8fe | ||
|
0cc77f99ac | ||
|
622049dfad | ||
|
65bcc58261 | ||
|
9749a2b9b7 | ||
|
7b365194ba | ||
|
6e5c2730f1 | ||
|
8d045f0c96 | ||
|
c370358bd1 | ||
|
03e8d921d3 | ||
|
6cf624c18d | ||
|
41acdce165 | ||
|
c607444c23 | ||
|
940198b9a0 | ||
|
9c3cf705c6 | ||
|
6027725fae | ||
|
054aa52bc8 | ||
|
f4a562bc3c | ||
|
de8bf33ad9 | ||
|
159eaf7899 | ||
|
1bc045eb18 | ||
|
0f934e48bc | ||
|
3ecd29d0b5 | ||
|
42498842b5 | ||
|
ed4fbf093d | ||
|
96b19027ec | ||
|
cdb8a1e98c | ||
|
8cd26c9380 | ||
|
0863f0d564 | ||
|
63a2f7b7a5 | ||
|
9b562276f3 | ||
|
a17e01793e | ||
|
7fb061ea76 | ||
|
91d864bded | ||
|
339d7d16c7 | ||
|
8e5762c3a3 | ||
|
50a811720f | ||
|
c2e1c4c1e1 | ||
|
e7b11a2d82 | ||
|
61d3582357 | ||
|
9e66219690 | ||
|
d049b8846c | ||
|
bdd760f343 | ||
|
1ee88618e8 | ||
|
e399474b4e | ||
|
e2e55b6e08 | ||
|
d0d2bad7f4 | ||
|
92bee549a1 | ||
|
02121f66de | ||
|
115eb6f511 | ||
|
d208da577f | ||
|
62665899c6 | ||
|
7640839f92 | ||
|
cda837247a | ||
|
8e2ba0d963 | ||
|
f4958de885 | ||
|
3917e3469d | ||
|
5ce79b1761 | ||
|
7a621e0cd7 | ||
|
34a1c965be | ||
|
17b42ae051 | ||
|
55ae00af27 | ||
|
7e200b1ec7 | ||
|
f65a3be231 | ||
|
20bec7e26c | ||
|
b367955e56 | ||
|
84c3ef144d | ||
|
ab2c8e3826 | ||
|
f2c6ff7904 | ||
|
06ee4d08d6 | ||
|
e96679c820 | ||
|
105e4652c6 | ||
|
e5b8f149bf | ||
|
334cb23691 | ||
|
52d9807648 | ||
|
b44a2075a8 | ||
|
b60952d684 | ||
|
5db362e812 | ||
|
0b74168343 | ||
|
58e2d7be61 | ||
|
728c167660 | ||
|
982f274425 | ||
|
557c7a2877 | ||
|
dbba7af4b5 | ||
|
82eefca110 | ||
|
778b30b1cf | ||
|
2d4616da01 | ||
|
37a12326dc | ||
|
5516b3284c | ||
|
ed0fae36ad | ||
|
35d27f03bd | ||
|
1600d27db4 | ||
|
0feca7ee7f | ||
|
6e0bb6cf8b | ||
|
e6d66eba7d | ||
|
bd6e77bf32 | ||
|
9027f3fb10 | ||
|
95eb302a62 | ||
|
0be982c798 | ||
|
67e5451c7c | ||
|
b31721b836 | ||
|
03146ed85f | ||
|
3007918647 | ||
|
c6c4e950db | ||
|
40eba9e95e | ||
|
51ce7c939d | ||
|
972e5aed8a | ||
|
8ba9e9efd8 | ||
|
63ca925786 | ||
|
71d1e6eb82 | ||
|
8641f69dae | ||
|
120aa066c2 | ||
|
27bcf6badb | ||
|
465691d5c9 | ||
|
48eb115cb9 | ||
|
51ee872179 | ||
|
c617f43e42 | ||
|
4920f64816 | ||
|
880134ce80 | ||
|
6e47652566 | ||
|
cb2df32d91 | ||
|
80fff62b34 | ||
|
0f6867662c | ||
|
3bb431f18e | ||
|
91a3adf2a2 | ||
|
e27d059376 | ||
|
22c61ba603 | ||
|
6807f7e07b | ||
|
8632ef34eb | ||
|
47c0d9500a | ||
|
ee01d26915 | ||
|
0910dc4e54 | ||
|
96cf42fb06 | ||
|
8b22f03c09 | ||
|
299604838d | ||
|
e9e4a0d0db | ||
|
c2049b21fc | ||
|
a415b9d7ae | ||
|
cb4fd1ebf9 | ||
|
a28b790256 | ||
|
55847fa22f | ||
|
6ca0e6adcc | ||
|
6542a60d61 | ||
|
ef4e1c53b7 | ||
|
ec64bf2f0f | ||
|
54dc2219fe | ||
|
a576ad2a39 | ||
|
25a246b6ae | ||
|
4840671f0e | ||
|
1d284a3528 | ||
|
721dd17686 | ||
|
3830f5ee46 | ||
|
ef8af13500 | ||
|
16fb1bda53 | ||
|
e8fcb0cc87 | ||
|
0e23ea5284 | ||
|
f9143ad817 | ||
|
925c03acca | ||
|
421d7a947f | ||
|
7104d5b480 | ||
|
ddb631d800 | ||
|
6107af625f | ||
|
2ce40e0390 | ||
|
3f267f3774 | ||
|
91ef985353 | ||
|
cc759afe16 | ||
|
fd405457e4 | ||
|
65706980a2 | ||
|
04851284ca | ||
|
fbedc6b29b | ||
|
d228ff9fbd | ||
|
0f28060795 | ||
|
1df8509e4d | ||
|
1229b772fe | ||
|
3cf3cd1598 | ||
|
c5f1f11f2d | ||
|
b4b424bf8a | ||
|
2d1aa27955 | ||
|
1f803662d3 | ||
|
2cbbc7100a | ||
|
ed2a6f15fd | ||
|
1aa806df85 | ||
|
17aa5ae243 | ||
|
1a96ee4c6b | ||
|
0d9ff26d51 | ||
|
ce40813bcf | ||
|
ea1db84fcc | ||
|
c79ee74b4c | ||
|
3407a31cf6 | ||
|
2dfe693564 | ||
|
e99edd4af9 | ||
|
8d43e1215a | ||
|
6449289147 | ||
|
5a33dcaf07 | ||
|
2e804086eb | ||
|
5bbedc7e3c | ||
|
9455936f19 | ||
|
9af4ec7b04 | ||
|
fb619a0099 | ||
|
35e369ff1a | ||
|
2fa8e2ff09 | ||
|
eeb9999334 | ||
|
e6cbd8c8f8 | ||
|
5a40204cb3 | ||
|
805cf06af7 | ||
|
bdce832fee | ||
|
4c97aa8542 | ||
|
ca9102d4f5 | ||
|
4f8e7b0e2b | ||
|
9e92c9230b | ||
|
2fef4a0c09 | ||
|
00dc711ffa | ||
|
a52c261d95 | ||
|
c63d8a70b8 | ||
|
79f5bcd2fc | ||
|
675ef8aefd | ||
|
f97bae0b27 | ||
|
3361901cd8 | ||
|
3db47b575b | ||
|
b1ab59b2e6 | ||
|
652b852f6d | ||
|
54fd2049a8 | ||
|
06d807d9ff | ||
|
b66b1ba9e6 | ||
|
06f71cea24 | ||
|
88eed12c78 | ||
|
ea75f65e35 | ||
|
b1a9b448a6 | ||
|
c27c3c48ba | ||
|
fdf28c3513 | ||
|
018222a1f4 | ||
|
ca5d6c5cfe | ||
|
d02488f00c | ||
|
1d14c8a706 | ||
|
573b744293 | ||
|
4bd326f72c | ||
|
d278809659 | ||
|
8053e11b9a | ||
|
5f544fba8a | ||
|
8ee0d8a73e | ||
|
b56352cb4e | ||
|
6d0cc97635 | ||
|
384c7cd831 | ||
|
9ec1e84ed0 | ||
|
691521c5b9 | ||
|
a1e77b4ccd | ||
|
f90da61a85 | ||
|
0b0ff15dde | ||
|
9b157c7ac3 | ||
|
525ba12fc8 | ||
|
2785b8ffc5 | ||
|
cd9d5db3c2 | ||
|
d8ef15a1e2 | ||
|
f1015ca73d | ||
|
e865a5d7c6 | ||
|
cd6e5ccfb8 | ||
|
46ca24cf9d | ||
|
813137efc9 | ||
|
4235288c55 | ||
|
7fd445173d | ||
|
1e190bbe70 | ||
|
0229686203 | ||
|
1606076b4e | ||
|
cd630e9516 | ||
|
3b200e5c92 | ||
|
639712c003 | ||
|
1abff212f9 | ||
|
cfb59f2f19 | ||
|
df63572b7c | ||
|
d89071dc03 | ||
|
95e0e8d459 | ||
|
6973952892 | ||
|
56cf5271a2 | ||
|
d679ceeb9b | ||
|
49cb391486 | ||
|
cfdde4c2ce | ||
|
57864a388a | ||
|
74c8a38d48 | ||
|
cd19cedd46 | ||
|
3f23d8ed06 | ||
|
8cbb0d03e8 | ||
|
52b99a1520 | ||
|
7eabe74402 | ||
|
10cf80d6ca | ||
|
bccfd0bcbd | ||
|
fa9e2bd6dd | ||
|
b15b3d6ac5 | ||
|
c4fb5b72cd | ||
|
fd706c3dbc | ||
|
5128190942 | ||
|
c6f127b48e | ||
|
011d66356f | ||
|
d24a974219 | ||
|
82f5e26ef4 | ||
|
247182edbf | ||
|
69fc518e39 | ||
|
aef4a88d7f | ||
|
69e70d88f4 | ||
|
aac0a45beb | ||
|
d04d89d622 | ||
|
0f1633dfbf | ||
|
737c3f62db | ||
|
49701fbc55 | ||
|
5395d57df8 | ||
|
8a7e3fe36f | ||
|
7bc0a52cc9 | ||
|
f428cc26a2 | ||
|
cf6c141e57 | ||
|
3a29b5f321 | ||
|
ffdae41605 | ||
|
688edd4b32 | ||
|
d687ef5c09 | ||
|
59c8a87047 | ||
|
759d59c2e6 | ||
|
73d12e1ed5 | ||
|
fcda843778 | ||
|
dcbd8aed30 | ||
|
154c838e9f | ||
|
f90f688299 | ||
|
fca9b57af5 | ||
|
59eed8395d | ||
|
a4ccd2d43a | ||
|
5136e7a0e0 | ||
|
423243c84b | ||
|
2042deb2bf | ||
|
e1216dce4b | ||
|
3839c7818e | ||
|
90652e40b4 | ||
|
83c1c20ce3 | ||
|
0bb1484b2d | ||
|
5881cee167 | ||
|
101935e594 | ||
|
9dda65de20 | ||
|
a00e35fb57 | ||
|
2c85b55ff8 | ||
|
d1b51b409a | ||
|
c3d5463589 | ||
|
710447f879 | ||
|
c61e4cae4d | ||
|
333aebf32c | ||
|
2657c8736e | ||
|
5b28f6b25f | ||
|
715253da2f | ||
|
66356d83ab | ||
|
8e531ca87a | ||
|
18c46851fd | ||
|
5456dbbf16 | ||
|
5c54b83108 | ||
|
cbf7f03bff | ||
|
ea05fea234 | ||
|
f78729ad1f | ||
|
4ec9492f7c | ||
|
f86b953420 | ||
|
d8f91a2ece | ||
|
6563606799 | ||
|
c01bc1c363 | ||
|
6f8c634756 | ||
|
22a93994aa | ||
|
e0013858c4 | ||
|
b6e0df57eb | ||
|
be210233be | ||
|
1a1a1d3841 | ||
|
4283bbde7f | ||
|
fba325f60e | ||
|
fcd76dabac | ||
|
f661cad6a3 | ||
|
1faac68441 | ||
|
e9366a0d41 | ||
|
953aa5d0b5 | ||
|
fc5782990f | ||
|
01163783ef | ||
|
be19a92f59 | ||
|
3fed1abc36 | ||
|
6973bf9331 | ||
|
e8867fa0f1 | ||
|
d9c15b84d0 | ||
|
81876c7bf3 | ||
|
ce6c7c8783 | ||
|
fad2ff674e | ||
|
1dd7727e97 | ||
|
fe0318584e | ||
|
8508fe737b | ||
|
c7a9e5f656 | ||
|
3e43fbce13 | ||
|
b304e2ae1f | ||
|
35f4ede11a | ||
|
65cbb6e388 | ||
|
960d7f84eb | ||
|
8a392e1c06 | ||
|
d9d75d6c71 | ||
|
69f9e38730 | ||
|
7f95186a97 | ||
|
b6db2bf2c5 | ||
|
6f976abf42 | ||
|
d80ffeeba1 | ||
|
c856e8d9ac | ||
|
6736bda429 | ||
|
0a8f9163db | ||
|
e078de25f0 | ||
|
cd284f78ad | ||
|
0a13c988fa | ||
|
b61bfd6ffe | ||
|
fc61b474cd | ||
|
bf25364333 | ||
|
bc7099d29b | ||
|
00c3ed715c | ||
|
701b57de89 | ||
|
e674d7d23b | ||
|
4a2d9bb028 | ||
|
ae3a278ac4 | ||
|
42ada7123c | ||
|
6d9f73eef7 | ||
|
541372eb91 | ||
|
8d7d70bc13 | ||
|
e77cc18468 | ||
|
193978a8be | ||
|
853ac0cca8 | ||
|
589f0803e6 | ||
|
fc67a44f95 | ||
|
204365b2ae | ||
|
2a63ecef2a | ||
|
1d660d7141 | ||
|
69000c0fc5 | ||
|
c8a0138b3b | ||
|
90de63e650 | ||
|
6b6e17cbad | ||
|
071bd2b0ca | ||
|
f74db06176 | ||
|
a6b3ddc7ed | ||
|
4ff889d4ec | ||
|
12b9f1b969 | ||
|
59bb1015ab | ||
|
f9c77171cf | ||
|
9628b64d3e | ||
|
d3f47a38b8 | ||
|
8111213691 | ||
|
a88ff902b4 | ||
|
1e57e1e486 | ||
|
172afb0789 | ||
|
5bfd1849c1 | ||
|
f61c9c93bb | ||
|
b0efdb6ee8 | ||
|
888a9ddaf4 | ||
|
d2482d6554 | ||
|
21cac248b3 | ||
|
ce7671151c | ||
|
b77525e570 | ||
|
e93cc50d1c | ||
|
c534305c7b | ||
|
797b71d903 | ||
|
74fd9e1e9e | ||
|
ff94a95589 | ||
|
0f5192bf19 | ||
|
eabbbba0c7 | ||
|
840cdf5512 | ||
|
757a6854ab | ||
|
b16dd051f1 | ||
|
68e73b658a | ||
|
af5f301276 | ||
|
2b9818a94d | ||
|
58200ecdb1 | ||
|
acaf22fa11 | ||
|
f60d6e0748 | ||
|
ae9163e9bb | ||
|
cad6b7971f | ||
|
ee38590c35 | ||
|
f10695fb8f | ||
|
b44ecc1958 | ||
|
931e7a7795 | ||
|
df85bb189d | ||
|
27e1f53b5f | ||
|
266ddedaef | ||
|
44dd327d22 | ||
|
13be56a2e3 | ||
|
5d0ba460ae | ||
|
6988b3accb | ||
|
3e2676a8c6 | ||
|
0f88c947f1 | ||
|
07b175c2ee | ||
|
6132cf2128 | ||
|
37dbb81425 | ||
|
c1028ec2cf | ||
|
f98dc4e576 | ||
|
da044960f9 | ||
|
03522b48a5 | ||
|
1b31525773 | ||
|
64a1408d6c | ||
|
3a5e68fca0 | ||
|
48ce788118 | ||
|
34f0a2951a | ||
|
66546418e3 | ||
|
611be7c0fa | ||
|
4f3e031f4f | ||
|
554c46182a | ||
|
b5d5d02a9c | ||
|
4640b65b85 | ||
|
1615c6bdc8 | ||
|
c003b8d32d | ||
|
3a1f924fb1 | ||
|
583460c0a8 | ||
|
1550a52002 | ||
|
9bd3bc8404 | ||
|
8c1d13b364 | ||
|
7ad407a8c8 | ||
|
bd02704384 | ||
|
03831babcd | ||
|
cfe61bbb9b | ||
|
a3bfb19059 | ||
|
e5f116fd2d | ||
|
a7f3a70ba4 | ||
|
1607a57fb7 | ||
|
ecd7690743 | ||
|
47a2087a91 | ||
|
7653764403 | ||
|
b7c8d5b9b1 | ||
|
fdac132905 | ||
|
60b993412e | ||
|
697c914c77 | ||
|
8c17ecb3d9 | ||
|
4c93121102 | ||
|
5efe9b99c2 | ||
|
3249763091 | ||
|
60964a4224 | ||
|
805a5399b5 | ||
|
0a39e4ccb0 | ||
|
f8cb908e0f | ||
|
d2aceed34e | ||
|
e6d9d83d92 | ||
|
1a1c87b5c4 | ||
|
5bcec583bb | ||
|
692606292f | ||
|
1782e55c66 | ||
|
707828ca76 | ||
|
5b0a817d69 | ||
|
23e77efc83 | ||
|
03c7368d71 | ||
|
fdc80f0663 | ||
|
4e708ea021 | ||
|
49898d01b5 | ||
|
8036923aca | ||
|
76c8f777ca | ||
|
e84bafd034 | ||
|
37fc42eb73 | ||
|
a0a89112bd | ||
|
4842f83aae | ||
|
69b185ff5e | ||
|
d8c2f0cbba | ||
|
5b05f22476 | ||
|
8012d1fcfc | ||
|
40753296c4 | ||
|
7baeb223f3 | ||
|
affacf472f | ||
|
896925ca49 | ||
|
78628c0f42 | ||
|
ee650c1ef4 | ||
|
5b65fae49d | ||
|
2d5d8ceb1d | ||
|
de214c8695 | ||
|
ca2446aef0 | ||
|
0ca96ee8be | ||
|
0c82c108ec | ||
|
15f09ef169 | ||
|
45cca9e002 | ||
|
76ecfd0234 | ||
|
927d46b2e4 | ||
|
3353598041 | ||
|
e0b7cbd70b | ||
|
21d06ad9a6 | ||
|
9e227200af | ||
|
bac546c090 | ||
|
058b6265a6 | ||
|
d4234b8921 | ||
|
18762cd4d5 | ||
|
9a2a223650 | ||
|
a0ad2b24b1 | ||
|
211c02c7de | ||
|
a3279ab164 | ||
|
d0ce79d81b | ||
|
e896be48f0 | ||
|
a806d753c4 | ||
|
ab4fd9ed53 | ||
|
9c98fb5312 | ||
|
c0dd8d0332 | ||
|
60a1f96b4f | ||
|
40dfb730dc | ||
|
4e72a8f3a5 | ||
|
3da32f007e | ||
|
4af8bb8e8d | ||
|
a6f190766c | ||
|
fa3f0d2071 | ||
|
8d6cfdc292 | ||
|
48f0c1c37b | ||
|
17584b52af | ||
|
c05bc899eb | ||
|
c9f6ce7496 | ||
|
67d2c454ee | ||
|
83c3c027d0 | ||
|
3cac187023 | ||
|
3800b0bf47 | ||
|
41b2eb8871 | ||
|
bc84cdda7d | ||
|
6d77394196 | ||
|
968bba4d8e | ||
|
cdd8090f33 | ||
|
1be26e4497 | ||
|
cc51ba4f90 | ||
|
85984109b5 | ||
|
689a12cdfb | ||
|
abd74db112 | ||
|
cb06dab16f | ||
|
d2a7e46c1a | ||
|
2997b0f2c4 | ||
|
f450ef09cc | ||
|
316746605e | ||
|
5522d11eed | ||
|
67266af1ae | ||
|
273c0f432c | ||
|
d346cdb897 | ||
|
57a4b48c9b | ||
|
c3b1018929 | ||
|
a2ba64ccf5 | ||
|
26d91e355a | ||
|
7bf394d8d5 | ||
|
cdf6f9c436 | ||
|
dece5d8de7 | ||
|
33b8da98af | ||
|
0386534b3a | ||
|
885858dcb4 | ||
|
8c5d82fab2 | ||
|
c683c1b8ec | ||
|
cde5aa9b06 | ||
|
d3ba6798d7 | ||
|
1f42c10c2a | ||
|
14f6108e57 | ||
|
9512a08810 | ||
|
78b8648945 | ||
|
71e01e5566 | ||
|
01614b5a13 | ||
|
7153c06e88 | ||
|
30969f8ab6 | ||
|
19f9c4f540 | ||
|
5b5e07d4dc | ||
|
4ab1fb3ec5 | ||
|
9dedb832fc | ||
|
b8b487991d | ||
|
f10ba28e0c | ||
|
22398e69c5 | ||
|
0eb51e6bb0 | ||
|
9436075fdf | ||
|
409494afbb | ||
|
a544ebd451 | ||
|
8350a6cc12 | ||
|
b5e7dc5304 | ||
|
a11171d73a | ||
|
4706abbc06 | ||
|
58b307ba02 | ||
|
3281ab05b1 | ||
|
3a32e79001 | ||
|
d91b3624e9 | ||
|
d02ced59f8 | ||
|
ba3abf7c4c | ||
|
cc24398e71 | ||
|
27d0a8f6e7 | ||
|
4788c46e2c | ||
|
0370c7b627 | ||
|
5cbae74086 | ||
|
008e30bdb7 | ||
|
a5f17fad58 | ||
|
22c3646fc6 | ||
|
a3e7f0b5ef | ||
|
9f5da92ab4 | ||
|
fdafe3b947 | ||
|
6dec711a0a | ||
|
ec67bc7f1a | ||
|
47aaf639b3 | ||
|
51233c2745 | ||
|
90bc84c010 | ||
|
7f1e684dab | ||
|
5f28d0ec24 | ||
|
348c4ad3a3 | ||
|
f90cc8d67d | ||
|
d3e9ffcaea | ||
|
b0a4a10dcc | ||
|
8ef5c96cb6 | ||
|
6be36ffe17 | ||
|
7d7975daf4 | ||
|
c98644b72f | ||
|
8a097fb79c | ||
|
f71e7f4fd3 | ||
|
9c8add97e7 | ||
|
b099c811cc | ||
|
a8ae0f8078 | ||
|
d355169b60 | ||
|
08295525de | ||
|
fdcf27fc65 | ||
|
f2c82b05d9 | ||
|
add9dda759 | ||
|
fbba4a1ec4 | ||
|
154d303463 | ||
|
fdef2db232 | ||
|
d49fa8e42b | ||
|
871d3cb87d | ||
|
f1d751b356 | ||
|
60c1939d26 | ||
|
d2aaac22e5 | ||
|
c6842a8591 | ||
|
60e9abdd61 | ||
|
9796630aa2 | ||
|
7230cdea33 | ||
|
6460907976 | ||
|
d7a50a1b48 | ||
|
4a88343372 | ||
|
74c2bbc2f0 | ||
|
5ed5d16716 | ||
|
43083b0b7a | ||
|
a4e5054008 | ||
|
02eb2f2e45 | ||
|
5d015bf746 | ||
|
ce5db5a5c1 | ||
|
cdcc3902c5 | ||
|
309835f2fe | ||
|
eca0ab0ef6 | ||
|
4ce35ee1ed | ||
|
8856456afd | ||
|
4259d900f4 | ||
|
438c448ef7 | ||
|
0fb5b35212 | ||
|
b69efb4970 | ||
|
44ea237538 | ||
|
a58316b24c | ||
|
df95d01f6e | ||
|
d6924893e5 | ||
|
0f04ea4f70 | ||
|
4b8026cf83 | ||
|
a98a586295 | ||
|
29ec7ba03a | ||
|
a35cc23d28 | ||
|
20a8ddd841 | ||
|
774fc9ce53 | ||
|
84ab395fae | ||
|
edc4dc5801 | ||
|
6e128a7285 | ||
|
ff977cebaf | ||
|
5bcd5f050a | ||
|
fe57f163f3 | ||
|
cb24db4e39 | ||
|
e93d96193c | ||
|
637a4dc1f9 | ||
|
2c522637ef | ||
|
8fbb1fd246 | ||
|
75b7e7d999 | ||
|
7e2e901035 | ||
|
a73a1f896c | ||
|
ae7f0e8ffb | ||
|
08343e6be9 | ||
|
9cb3496159 | ||
|
37b035d0d0 | ||
|
f37ac8b5de | ||
|
343d04b05d | ||
|
e92741edd6 | ||
|
8565cd7d40 | ||
|
58e299383d | ||
|
bcb44aa532 | ||
|
0c552feb57 | ||
|
cd1aa57243 | ||
|
c9098b0489 | ||
|
117ce52597 | ||
|
1836b9a0b0 | ||
|
563a4b7bd8 | ||
|
2a5648d1a7 | ||
|
5b425f9178 | ||
|
820e32ddca | ||
|
b873e706ca | ||
|
f0f6e7c856 | ||
|
1ff3c7d074 | ||
|
393bb8993e | ||
|
00ddb1eb64 | ||
|
cd518776a9 | ||
|
391f7d22a3 | ||
|
effbf10571 | ||
|
4405e94e0c | ||
|
69f1b72127 | ||
|
383fa2e278 | ||
|
573100c203 | ||
|
348efdd7b6 | ||
|
cb9cc95e6a | ||
|
1994b334fa | ||
|
3e5a9cdb97 | ||
|
ae863c95c7 | ||
|
e89e5efb62 | ||
|
9f2ce06829 | ||
|
ec89ebc6b0 | ||
|
f3e9668eb7 | ||
|
cc7e2fc456 | ||
|
172d925f0f | ||
|
8a8ecaade3 | ||
|
d21a3f0bca | ||
|
9322212601 | ||
|
691eefec2f | ||
|
50b66cceca | ||
|
c7c57ab2a5 | ||
|
558b6443bc | ||
|
335b43ead4 | ||
|
e648392330 | ||
|
ab29d7e872 | ||
|
d7fae6b5b8 | ||
|
4ba4910a9c | ||
|
fe9a037cf9 | ||
|
2f14832c34 | ||
|
52ace55db0 | ||
|
224a4d786b | ||
|
3ea97ad9ff | ||
|
e7a56bb2b1 | ||
|
e4aed469d7 | ||
|
15a42a3729 | ||
|
0b9e007b46 | ||
|
88561a6c3c | ||
|
e6b77c3381 | ||
|
f7e1589476 | ||
|
0379523eaf | ||
|
5db7502fe4 | ||
|
1578188fde | ||
|
e8e1a0fe4d | ||
|
8059009ff3 | ||
|
32865f065c | ||
|
e03ef78049 | ||
|
45a820bb35 | ||
|
3c26159b79 | ||
|
cf67e0d4af | ||
|
1b2767167d | ||
|
f07bb5c275 | ||
|
0e2cc418e2 | ||
|
0340456d55 | ||
|
7aea2cec69 | ||
|
fa83107840 | ||
|
bb0bf46f81 | ||
|
694c3fed2d | ||
|
772e39b710 | ||
|
05e0a2c29d | ||
|
78e0efb95f | ||
|
a5f749cfd2 | ||
|
3d6c932e1b | ||
|
4488a1b95f | ||
|
e2808a90c6 | ||
|
755a972e02 | ||
|
8b1de22049 | ||
|
a9735123b7 | ||
|
5ccafa2838 | ||
|
e2926a4f82 | ||
|
09df315639 | ||
|
5a9b3b6c7c | ||
|
6da6f35ddd | ||
|
dcb5590842 | ||
|
5135c8a782 | ||
|
1b2f4fb036 | ||
|
76a4a445f0 | ||
|
20abb125a5 | ||
|
be9e50b4a1 | ||
|
02bfcf577d | ||
|
038efa10f2 | ||
|
dfa692569b | ||
|
9abc6a2e44 | ||
|
11f23f4e00 | ||
|
6a7d40d171 | ||
|
bf5601429e | ||
|
73ae8aaf2f | ||
|
7eb7bd98f3 | ||
|
10e22654ea | ||
|
ccfc80ad04 | ||
|
5e95278084 | ||
|
d69ba6bc75 | ||
|
d28fbe2d3b | ||
|
415e7b84da | ||
|
ac4986b620 | ||
|
9a4f4cbb65 | ||
|
8e32957111 | ||
|
91aa3703d4 | ||
|
a889762056 | ||
|
6478d6c9c9 | ||
|
7a681d0a4a | ||
|
83b6eba8bd | ||
|
2766ded5f6 | ||
|
ca0d966ed9 | ||
|
386c583b0e | ||
|
b3d34ad7e9 | ||
|
ba5c636dda | ||
|
ea16f6e0b1 | ||
|
0dd0dc5489 | ||
|
f3ab21ff43 | ||
|
5ac6b46efd | ||
|
6548f30a4b | ||
|
849d7983f6 | ||
|
e32e4423db | ||
|
7f5bf131a8 | ||
|
87a639df2b | ||
|
bdd8eab87e | ||
|
b9e5c8a387 | ||
|
d646a2dd01 | ||
|
71a3a0176e | ||
|
a363d119cf | ||
|
ff08bdbc0b | ||
|
71efff6827 | ||
|
7595fe4a8c | ||
|
7697e75466 | ||
|
b8fbaa2029 | ||
|
38bc750ecf | ||
|
6cfacb1a48 | ||
|
0b9078d034 | ||
|
86dc72b204 | ||
|
64b7bc3d62 | ||
|
086f9e1123 | ||
|
4fe70fe8ee | ||
|
95a1f40404 | ||
|
88f04509ee | ||
|
7eebd544d6 | ||
|
e369a8decf | ||
|
ad8054ba4b | ||
|
b47cb4e29a | ||
|
8c42ddf6c3 | ||
|
f7fcabbf41 | ||
|
38a51070fc | ||
|
44fa099a77 | ||
|
af6f3999df | ||
|
6fbeb35951 | ||
|
b2eadffde0 | ||
|
200ba2775f | ||
|
39ba021a79 | ||
|
2c6b291b9b | ||
|
770846be2e | ||
|
af4aa01b75 | ||
|
1bf2bec805 | ||
|
3a41ac9be0 | ||
|
f2c9cdd7f1 | ||
|
058f418cc7 | ||
|
5fa84b0dfb | ||
|
ec8ec3e38d | ||
|
0e6190b42b | ||
|
fcfee36c8c | ||
|
1d3d3b99bb | ||
|
2f80bf9647 | ||
|
675a4c795f | ||
|
ed3361df57 | ||
|
87396572e4 | ||
|
143b610291 | ||
|
c952c4591f | ||
|
e418701e68 | ||
|
6fc0ee3062 | ||
|
7b42d7d2c8 | ||
|
eb083ae925 | ||
|
905e65365f | ||
|
a0c04c0e6a | ||
|
635baa5b6f | ||
|
7591b614c5 | ||
|
5b5ec682c0 | ||
|
b0a80ddf65 | ||
|
893531f8c7 | ||
|
3d498e7a75 | ||
|
45ad201132 | ||
|
6e64b950ca | ||
|
6d4e67769c | ||
|
3aa0394062 | ||
|
b65767a43c | ||
|
3a9817d11e | ||
|
6a42ef50ec | ||
|
35b6b3fc46 | ||
|
f5515e3496 | ||
|
fd509bb4af | ||
|
b21c0f5d69 | ||
|
1033b3626f | ||
|
f407c8d756 | ||
|
6436dff94b | ||
|
3f7b4e10b6 | ||
|
36168a9a5d | ||
|
118540c733 | ||
|
cd5d412c69 | ||
|
e29fb3eb66 | ||
|
8ff1668c8f | ||
|
14fc73b388 | ||
|
f34330f101 | ||
|
2ba50321d4 | ||
|
38177c7e54 | ||
|
687e308a73 | ||
|
490db0db62 | ||
|
feb5f17479 | ||
|
fbb3543818 | ||
|
30d0dfb424 | ||
|
7239a2013c | ||
|
2361c5a5e7 | ||
|
38e8a881d5 | ||
|
d45d1b4225 | ||
|
e9c88dfdc4 | ||
|
fbb0ac5892 | ||
|
7a902bbd25 | ||
|
c75618eb8f | ||
|
74ee629266 | ||
|
8ecc58e1bf | ||
|
71078637db | ||
|
5460a977b1 | ||
|
a0dd0702fb | ||
|
3e48992eb4 | ||
|
bdb31f2890 | ||
|
0255546ae1 | ||
|
d066350eea | ||
|
a1e3260309 | ||
|
be1f57a8a6 | ||
|
ca4dfe0f0f | ||
|
fa69957d3f | ||
|
f7203ed7e2 | ||
|
331858bd4f | ||
|
4d2c9523a3 | ||
|
4d9d84a8f3 | ||
|
ec1089870f | ||
|
dbedefc021 | ||
|
f6b0da3f5e | ||
|
85bf89fd97 | ||
|
c2a80b12f0 | ||
|
981c97cca5 | ||
|
091cbc5355 | ||
|
e5a8114887 | ||
|
4779fc6f43 | ||
|
86e7daaec4 | ||
|
cab73a26e7 | ||
|
554ddb3b51 | ||
|
3b22bdb8ae | ||
|
5fdd07679e | ||
|
69f75a1520 | ||
|
f9b7acb8b1 | ||
|
91f700ad54 | ||
|
966aca7753 | ||
|
4de8816ed5 | ||
|
bea68a1056 | ||
|
6fea7116b6 | ||
|
2955914ca5 | ||
|
fd150b5b9d | ||
|
334ffad196 | ||
|
a796a9d179 | ||
|
917dcea28a | ||
|
c901228dc5 | ||
|
da21580785 | ||
|
4d92187327 | ||
|
207d433fb3 | ||
|
ffc311d7bd | ||
|
a1e8e71359 | ||
|
7942e74dc6 | ||
|
c4e21c2b6a | ||
|
0a06aa6f9f | ||
|
f985cfa25c | ||
|
6e143aef5c | ||
|
bf16aa6192 | ||
|
d96c58d192 | ||
|
e7e0264411 | ||
|
430a11282b | ||
|
9957b2798c | ||
|
2c5b0010bf | ||
|
1e3c6ed98c | ||
|
484f290eb0 | ||
|
06ad539941 | ||
|
5b84e80ac4 | ||
|
ca0f349348 | ||
|
d79089aea3 | ||
|
03611ad5ca | ||
|
c78c1cd3cf | ||
|
55afa7b5b7 | ||
|
a6c407b62b | ||
|
21897a3cd4 | ||
|
5796dd2098 | ||
|
d050071437 | ||
|
eaf8510f49 | ||
|
6ee2595dce | ||
|
0ecf2d6d9a | ||
|
50fd075168 | ||
|
6fa76bb83a | ||
|
b175c1fc94 | ||
|
b461625518 | ||
|
3819897ba1 | ||
|
6a031857ba | ||
|
0e57b7eb46 | ||
|
e21a0ba5b7 | ||
|
78f731e616 | ||
|
f6688a65ae | ||
|
035b9c6d1a | ||
|
266ff244d9 | ||
|
de1bfa5c34 | ||
|
478b8f00d8 | ||
|
860d2fd7f5 | ||
|
6ce091f4d8 | ||
|
ce595ac5e4 | ||
|
267436a00d | ||
|
97e0f4258a | ||
|
faa6ccc150 | ||
|
349cade946 | ||
|
0200aa92de | ||
|
60ed2cbee5 | ||
|
0e7b2f36c8 | ||
|
4083221b21 | ||
|
d55c511b4a | ||
|
7863e1dffe | ||
|
7cfdf778de | ||
|
075e55c077 | ||
|
dc818e8a0c | ||
|
caa58dd913 | ||
|
c84c3cd026 | ||
|
bda5ac9fb5 | ||
|
f928f645e5 | ||
|
53d7e906d4 | ||
|
e670b5d03f | ||
|
2a7d555859 | ||
|
e6675f500c | ||
|
c3f20d3ebc | ||
|
ff257060d1 | ||
|
68cde202a4 | ||
|
1a7a87e170 | ||
|
2990642923 | ||
|
0818de036e | ||
|
122bc29035 | ||
|
861cf8a1b2 | ||
|
7d6959e9e4 | ||
|
85091052e7 | ||
|
f46ad5bfda | ||
|
29f7937fc2 | ||
|
7fae9fda10 | ||
|
a37961dccc | ||
|
55155646aa | ||
|
7bffe86439 | ||
|
86136e0c6c | ||
|
21ae447d9c | ||
|
ad211b4b4e | ||
|
d6218f35fe | ||
|
7af0dcd19a | ||
|
e1a52c5606 | ||
|
468c345527 | ||
|
041bdc08a2 | ||
|
7535f1a8c6 | ||
|
0b24433db3 | ||
|
f7de0c5ffd | ||
|
62c48b9a46 | ||
|
3fafda9684 | ||
|
2cf1cda128 | ||
|
fedee9499b | ||
|
7c6c7f34ba | ||
|
0d65edd0e8 | ||
|
d57520c66a | ||
|
fa28ae1433 | ||
|
0acf9823e5 | ||
|
799ad5f1da | ||
|
9f6b3ccf40 | ||
|
df714c357d | ||
|
de5fdc84f8 | ||
|
b5b228af15 | ||
|
7a85628f46 | ||
|
075e76fc4b | ||
|
172a98fed4 | ||
|
409bdd7b78 | ||
|
d012c689e1 | ||
|
89ec0629e6 | ||
|
b3475ae195 | ||
|
a72ab67473 | ||
|
90fbfec914 | ||
|
49ef023a90 | ||
|
64bfd74f18 | ||
|
655e35056e | ||
|
a13ba63ab8 | ||
|
e2e00d4c87 | ||
|
159dc44f6c | ||
|
8fe2091926 | ||
|
c362c38dc0 | ||
|
316a52bbc2 | ||
|
0de9f6f944 | ||
|
3ba021fdd9 | ||
|
04c6579f2c | ||
|
bc7bd456a7 | ||
|
6d7511efbb | ||
|
a0fb99d78f | ||
|
685a52c2c5 | ||
|
fc77419ca1 | ||
|
bab3a12e92 | ||
|
21f0b90f48 | ||
|
83682ab513 | ||
|
7b987b5a8f | ||
|
cb5aa425c8 | ||
|
dd222381dd | ||
|
d7210811f6 | ||
|
3a43cfeebf | ||
|
31e33c6628 | ||
|
875f8d6997 | ||
|
aaaf27af6e | ||
|
43624ffa75 | ||
|
996b6623f1 | ||
|
613041b806 | ||
|
ff1d01a864 | ||
|
bcbbe10bf8 | ||
|
3de0abfc84 | ||
|
133fd03469 | ||
|
4f97c5c017 | ||
|
2d8fbac58c | ||
|
95f7ac4a4a | ||
|
6a56a6ccf0 | ||
|
1eb8df9fa6 | ||
|
485b4baebb | ||
|
5590d887c9 | ||
|
d00bfa997e | ||
|
1a8590e9b9 | ||
|
5a978977df | ||
|
5d763c7e6c | ||
|
050b089e72 | ||
|
733ea4027c | ||
|
10500c78b1 | ||
|
0040cc02a2 | ||
|
74afd20f0c | ||
|
dc9bde2184 | ||
|
5243ea7eb2 | ||
|
7d93492658 | ||
|
c6b56345f5 | ||
|
8a6fe20a69 | ||
|
6bcbedd6a4 | ||
|
4c935b78f9 | ||
|
94cddda6d0 | ||
|
1924ea062c | ||
|
07e88b2eeb | ||
|
e4a08c29d7 | ||
|
d60afcaabe | ||
|
1a091460ca | ||
|
8ebaa7f6eb | ||
|
e2a64e21a2 | ||
|
78ddd22f09 | ||
|
c546b7076a | ||
|
0e2bb5d784 | ||
|
5c069c38f5 | ||
|
451b9c0ae9 | ||
|
047897621a | ||
|
52e5cec585 | ||
|
bc98b65efa | ||
|
9250e77862 | ||
|
cd69e71945 | ||
|
e705e6e990 | ||
|
4638f11c5e | ||
|
9de7c15e93 | ||
|
61736546b4 | ||
|
82b6dd23a7 | ||
|
64427f0160 | ||
|
cbf47cb9ee | ||
|
dba3e4202d | ||
|
173cd6de4d | ||
|
3d333bd8f2 | ||
|
a82f8c7dd0 | ||
|
cde079e44e | ||
|
f8d20b61ea | ||
|
d279435502 | ||
|
eb2065128e | ||
|
10e81aa476 | ||
|
3e808f05fd | ||
|
e57bfad9b1 | ||
|
5f344f07d4 | ||
|
e1b7b64e1b | ||
|
ed3970be81 | ||
|
c27861d40f | ||
|
565dfba8c9 | ||
|
553a26eb63 | ||
|
9c85bc2cd1 | ||
|
e96bc9c48a | ||
|
0d036d8183 | ||
|
e5f2a7f2f5 | ||
|
016ce3dd61 | ||
|
afb375955e | ||
|
b702b0b40c | ||
|
91ecab51c5 | ||
|
7628506926 | ||
|
4e7f7c0c9c | ||
|
863f5bcf18 | ||
|
dccd489fcc | ||
|
333c3f6369 | ||
|
808413d0bf | ||
|
440be3d920 | ||
|
9b27a4f601 | ||
|
6e36ca32b4 | ||
|
85a9c303f2 | ||
|
7e9972d99f | ||
|
771975cd35 | ||
|
356f8e580b | ||
|
68268cb35d | ||
|
8e58ed12af | ||
|
beb428b246 | ||
|
4d81caff0b | ||
|
78372c9bac | ||
|
a4db1bc671 | ||
|
8ea1c373ed | ||
|
8ef27dfcdc | ||
|
23e43c7bc1 | ||
|
edb8f4c79f | ||
|
e115fa4bb3 | ||
|
52a2daddbf | ||
|
c400142996 | ||
|
219171a18c | ||
|
d7d573d9dd | ||
|
c4b17f8919 | ||
|
da2f4b96c7 | ||
|
08af9de190 | ||
|
9e2982992a | ||
|
c03ac5c5f1 | ||
|
5934f2f08e | ||
|
f136f40fdc | ||
|
d213e4ab57 | ||
|
782aae4ddf | ||
|
4f47bd7bcd | ||
|
4f76946140 | ||
|
aa60923cbd | ||
|
7670ce32b1 | ||
|
0b98632336 | ||
|
49c998de83 | ||
|
da69d6b2c9 | ||
|
a757aefce0 | ||
|
b2129cb8c4 | ||
|
27f14c175f | ||
|
aad49c520b | ||
|
af794a16d6 | ||
|
ca7266cd69 | ||
|
fb758afc81 | ||
|
b806f0da49 | ||
|
4e3b1f3e13 | ||
|
475467c41c | ||
|
28e5039873 | ||
|
1efc6e6151 | ||
|
e280a02643 | ||
|
788dc42684 | ||
|
01f2759a62 | ||
|
9419e3af9c | ||
|
6d220005cc | ||
|
6d68e61bbd | ||
|
29582623b0 | ||
|
4571e7853a | ||
|
d6e59c5238 | ||
|
6335cbedb8 | ||
|
f3344ce05d | ||
|
155d38d24a | ||
|
412f6d8316 | ||
|
d0f3ed80e0 | ||
|
b86f3b74bd | ||
|
60054d1d8b | ||
|
c40c6428d7 | ||
|
e708ecccee | ||
|
04f991d3bf | ||
|
d5d7368ba0 | ||
|
abff929d4e | ||
|
49edc82594 | ||
|
7dd7d0ec17 | ||
|
61aaf64f95 | ||
|
da35785951 | ||
|
e394034678 | ||
|
edf002ab32 | ||
|
50f2076981 | ||
|
a9ae78bcde | ||
|
0d7f9a2ab3 | ||
|
a5ee120281 | ||
|
12ebf44e22 | ||
|
220f5d0967 | ||
|
4ef05272c4 | ||
|
c3262aa5f8 | ||
|
42119c8f42 | ||
|
27cce03968 | ||
|
776aba1e49 | ||
|
9356841cfc | ||
|
7fc233e808 | ||
|
7b2eac3abd | ||
|
93323deea5 | ||
|
e8fa399e9e | ||
|
a974eb5d9f | ||
|
6a3f04274d | ||
|
f8e1395087 | ||
|
82b5af51e5 | ||
|
cf40c14a86 | ||
|
be2b2b7836 | ||
|
0cebae8032 | ||
|
6ebc77cbba | ||
|
542a5e88b7 | ||
|
72b36522e8 | ||
|
252d937405 | ||
|
a73b0309b9 | ||
|
c22283e799 | ||
|
1e51ab4a59 | ||
|
0c2f2667d3 | ||
|
496c9ddb91 | ||
|
5ad0b2eed9 | ||
|
577d62e783 | ||
|
dcb73c9916 | ||
|
6b2910c3c7 | ||
|
db04906416 | ||
|
54fceeb07d | ||
|
fa32678645 | ||
|
ee1df1a396 | ||
|
729be8767c | ||
|
8471f0d86d | ||
|
390b818d71 | ||
|
1a1c190ea3 | ||
|
40d82b80f1 | ||
|
7647ecbbc7 | ||
|
b2a5cf9dd0 | ||
|
cbf3ab2ec2 | ||
|
c4a37ff0ba | ||
|
bdc7ea4346 | ||
|
d4c4b9b10a | ||
|
4b9105edff | ||
|
c0980a5a9e | ||
|
272055fc1d | ||
|
cbb1851b12 | ||
|
45d758d52e | ||
|
49936ef095 | ||
|
986d4be601 | ||
|
b6ad9719ad | ||
|
96a1546da1 | ||
|
6884dd2b9e | ||
|
9c027e59c4 | ||
|
9e16ff3644 | ||
|
4000cb5d1c | ||
|
1030b412ff | ||
|
54f0fbcf6b | ||
|
1602182085 | ||
|
20bb1238c5 | ||
|
68fbe8125e | ||
|
23e6e6e69d | ||
|
ade5af2559 | ||
|
0a993dc012 | ||
|
4baa46507f | ||
|
0d8878abd3 | ||
|
f962084591 | ||
|
921b45286b | ||
|
301bfed05e | ||
|
62e7d6fa86 | ||
|
7e34cd4452 | ||
|
b35cb9a7ab | ||
|
18b836525c | ||
|
e34e49b88d | ||
|
5bf879c2d9 | ||
|
61d6309c0e | ||
|
17c9c0f9ef | ||
|
9bd62fd3d4 | ||
|
de73d4baa7 | ||
|
379ff5163b | ||
|
d9538ccb08 | ||
|
9289dbdf77 | ||
|
5719ae495a | ||
|
a89e3b7924 | ||
|
e576d09712 | ||
|
43600a7824 | ||
|
921f01d9dc | ||
|
dec5668582 | ||
|
e30bc12354 | ||
|
fca4fa666d | ||
|
59478160c8 | ||
|
8110fe89ef | ||
|
c7ed1b5eae | ||
|
25951e7097 | ||
|
687b9d1bef | ||
|
630e77b9eb | ||
|
68ff2e17b3 | ||
|
945d83608a | ||
|
833d473268 | ||
|
2256b0c5ec | ||
|
677aa7d59b | ||
|
4363005e92 | ||
|
2e3ef618db | ||
|
1adbbd14c6 | ||
|
4dfd7db729 | ||
|
e70562a102 | ||
|
a0e5947ba4 | ||
|
e841ea8a91 | ||
|
770a435029 | ||
|
49b56e84a7 | ||
|
ff1a6fdbbd | ||
|
2168090b96 | ||
|
33f7b680bc | ||
|
0957298cb8 | ||
|
08f2f982cf | ||
|
3376fbfa55 | ||
|
4a31e5fe73 | ||
|
baacec5ba6 | ||
|
95188b71b8 | ||
|
cf57be6e98 | ||
|
b691092d7a | ||
|
03e60fc028 | ||
|
2e25ec318f | ||
|
7236e5e9ae | ||
|
6f7dd51a98 | ||
|
db1dab9293 | ||
|
06f1b9e4d7 | ||
|
8f958ac931 | ||
|
eae63bfb79 | ||
|
8adfc81c30 | ||
|
0c516443e3 | ||
|
ad9bc0bf63 | ||
|
b0596670a6 | ||
|
998011ff43 | ||
|
081e1c5b62 | ||
|
edfd2c4d54 | ||
|
9d65a3a34c | ||
|
1a86cbdb9d | ||
|
849cb4456c | ||
|
1736717f2e | ||
|
50b75e1e63 | ||
|
179005dd6c | ||
|
6b3fe6b2cb | ||
|
c34872bb26 | ||
|
4845a21287 | ||
|
ddd513fe09 | ||
|
4ce118d459 | ||
|
99da184ba5 | ||
|
e2e4e62c4f | ||
|
229dd7a6dd | ||
|
1e0c4cc5cd | ||
|
b40ccc7a15 | ||
|
7d6b7e588c | ||
|
22cacd2c5b | ||
|
6a22e7836a | ||
|
32a67b9b33 | ||
|
b5dff09c28 | ||
|
c56d8153e8 | ||
|
eb5950d126 | ||
|
4241436e40 | ||
|
e93af7aed5 | ||
|
d2416f850e | ||
|
7af22eb006 | ||
|
3f1be5e7e7 | ||
|
a4e649c82d | ||
|
cde1a8f0a8 | ||
|
cda6eb7c2f | ||
|
3c3e6aeca0 | ||
|
99156311db | ||
|
91b2e5e703 | ||
|
573fd816d0 | ||
|
e6aa002758 | ||
|
361d46ac5d | ||
|
9bc7c2fd65 | ||
|
ce49f5f8b7 | ||
|
81e4cd319d | ||
|
71f905535f | ||
|
d3bff0a9e3 | ||
|
88e6315d09 | ||
|
3d933c5244 | ||
|
73af19314d | ||
|
1d71712c8a | ||
|
1333c35389 | ||
|
e0f5f47a15 | ||
|
34323042d5 | ||
|
aa86fa9986 | ||
|
d2d659d5a9 | ||
|
1595248b52 | ||
|
fc94c616b5 | ||
|
f6f0bcf1c3 | ||
|
6eeeda5dab | ||
|
58993d2ead | ||
|
7c0b608769 | ||
|
259be84a3e | ||
|
57c880cf85 | ||
|
0469fe76d7 | ||
|
a3e74320fa | ||
|
4e0f4397b2 | ||
|
b26aab3863 | ||
|
75ec97fe61 | ||
|
9a356453fc | ||
|
f0020b3393 | ||
|
ea9f1dc91d | ||
|
2ef77dcf1f | ||
|
29e7ec4cc9 | ||
|
5710e3fad0 | ||
|
a03aa62d58 | ||
|
2203a39917 | ||
|
bc58668483 | ||
|
0b4650f355 | ||
|
aecdec48ad | ||
|
e49198a0d4 | ||
|
6aa4553dd3 | ||
|
860e8a8f5a | ||
|
434037d793 | ||
|
214eae27da | ||
|
0567188fa8 | ||
|
4326902683 | ||
|
da3589149d | ||
|
69b4b4d1f4 | ||
|
dd4b46a88b | ||
|
6f2253b2f4 | ||
|
4c00d9512e | ||
|
6f511cb1e6 | ||
|
029e84f538 | ||
|
42b1db1d08 | ||
|
9d4b34cad7 | ||
|
cd9aff0f59 | ||
|
032816fffc | ||
|
1408f06c8d | ||
|
49f8d0c6d8 | ||
|
0d6443c30a | ||
|
e62d91dda8 | ||
|
0e122c9dc5 | ||
|
c2bd453e8c | ||
|
949ea7afb7 | ||
|
9f575101d2 | ||
|
2b4470054e | ||
|
b220613e54 | ||
|
11328babe8 | ||
|
735bc92bc4 | ||
|
6894b9b1b7 | ||
|
4de7815f31 | ||
|
4ce7b48468 | ||
|
77e4cb87ad | ||
|
dd6b43035e | ||
|
e76094c546 | ||
|
9c00dc4cab | ||
|
fc416de348 | ||
|
a2156be4ec | ||
|
cf427eba0f | ||
|
0050c90678 | ||
|
5eeff02dbe | ||
|
eb283efc20 | ||
|
87121c9c21 | ||
|
a2dbbbf832 | ||
|
fd251cd9a9 | ||
|
8ced2d288e | ||
|
242926d381 | ||
|
8c9a148e71 | ||
|
9e165fc7dc | ||
|
f46e4e0cec | ||
|
efcf5ecef4 | ||
|
b6ba843d09 | ||
|
915112e274 | ||
|
cc8f8fcd2c | ||
|
bda3ea9a35 | ||
|
a7aed1f93a | ||
|
a33f602f3b | ||
|
4d08b49578 | ||
|
21e049ab45 | ||
|
1d4234caea | ||
|
d1269de3a7 | ||
|
8c19fcf8fc | ||
|
beaba2be55 | ||
|
32c9ad1d59 | ||
|
a194513252 | ||
|
cd93a1d1e1 | ||
|
2867728996 | ||
|
394cc78851 | ||
|
76f0a26322 | ||
|
92d7a577a0 | ||
|
9c1219b188 | ||
|
f62bbfe286 | ||
|
fef176eb96 | ||
|
ed541dac3b | ||
|
4a175b2158 | ||
|
4076899e11 | ||
|
998bdf3b56 | ||
|
76b211eb6d | ||
|
2a4812cb87 |
182
.eslintignore
Normal file
@@ -0,0 +1,182 @@
|
||||
*.min.js
|
||||
.git/
|
||||
.github/
|
||||
_mydocs/
|
||||
_releases/
|
||||
Assets/
|
||||
CliClient/build
|
||||
CliClient/locales
|
||||
CliClient/locales-build
|
||||
CliClient/node_modules
|
||||
CliClient/tests-build
|
||||
CliClient/tests/enex_to_md
|
||||
CliClient/tests/html_to_md
|
||||
CliClient/tests/logs
|
||||
CliClient/tests/support
|
||||
CliClient/tests/sync
|
||||
CliClient/tests/tmp
|
||||
Clipper/content_scripts/JSDOMParser.js
|
||||
Clipper/content_scripts/Readability-readerable.js
|
||||
Clipper/content_scripts/Readability.js
|
||||
Clipper/dist
|
||||
Clipper/icons
|
||||
Clipper/popup/build
|
||||
Clipper/popup/config/webpack.config.js
|
||||
Clipper/popup/config/webpack_config_at_eject_time.js
|
||||
Clipper/popup/node_modules
|
||||
Clipper/popup/scripts/build.js
|
||||
docs/
|
||||
ElectronClient/dist
|
||||
ElectronClient/lib
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/plugins/lists.js
|
||||
ElectronClient/lib/vendor/sjcl-rn.js
|
||||
ElectronClient/lib/vendor/sjcl.js
|
||||
ElectronClient/locales
|
||||
ElectronClient/node_modules
|
||||
ElectronClient/packageInfo.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/supportedLocales.js
|
||||
highlight.pack.js
|
||||
Modules/TinyMCE/JoplinLists/
|
||||
node_modules/
|
||||
ReactNativeClient/android
|
||||
ReactNativeClient/ios
|
||||
ReactNativeClient/lib/joplin-renderer/assets/
|
||||
ReactNativeClient/lib/joplin-renderer/vendor/fountain.min.js
|
||||
ReactNativeClient/lib/rnInjectedJs/
|
||||
ReactNativeClient/lib/vendor/
|
||||
ReactNativeClient/lib/welcomeAssets.js
|
||||
ReactNativeClient/locales
|
||||
ReactNativeClient/node_modules
|
||||
ReactNativeClient/pluginAssets/
|
||||
readme/
|
||||
Server/.git/
|
||||
Server/.github/
|
||||
Server/bin/
|
||||
Server/dist/
|
||||
Server/docs/
|
||||
Server/node_modules/
|
||||
Tools/node_modules
|
||||
Tools/PortableAppsLauncher
|
||||
Modules/TinyMCE/IconPack/postinstall.js
|
||||
Modules/TinyMCE/langs/
|
||||
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
CliClient/app/LinkSelector.js
|
||||
CliClient/build/LinkSelector.js
|
||||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.js
|
||||
ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/ErrorBoundary.js
|
||||
ElectronClient/gui/Header/commands/focusSearch.js
|
||||
ElectronClient/gui/MainScreen/commands/editAlarm.js
|
||||
ElectronClient/gui/MainScreen/commands/exportPdf.js
|
||||
ElectronClient/gui/MainScreen/commands/hideModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/moveToFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newNote.js
|
||||
ElectronClient/gui/MainScreen/commands/newNotebook.js
|
||||
ElectronClient/gui/MainScreen/commands/newTodo.js
|
||||
ElectronClient/gui/MainScreen/commands/print.js
|
||||
ElectronClient/gui/MainScreen/commands/renameFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/renameTag.js
|
||||
ElectronClient/gui/MainScreen/commands/search.js
|
||||
ElectronClient/gui/MainScreen/commands/selectTemplate.js
|
||||
ElectronClient/gui/MainScreen/commands/setTags.js
|
||||
ElectronClient/gui/MainScreen/commands/showModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
ElectronClient/gui/NoteEditor/commands/focusElementNoteBody.js
|
||||
ElectronClient/gui/NoteEditor/commands/focusElementNoteTitle.js
|
||||
ElectronClient/gui/NoteEditor/commands/showLocalSearch.js
|
||||
ElectronClient/gui/NoteEditor/commands/showRevisions.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/AceEditor.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
ElectronClient/gui/NoteEditor/NoteEditor.js
|
||||
ElectronClient/gui/NoteEditor/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/contextMenu.js
|
||||
ElectronClient/gui/NoteEditor/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/resourceHandling.js
|
||||
ElectronClient/gui/NoteEditor/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/utils/useDropHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useFormNote.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
|
||||
ElectronClient/gui/NoteListItem.js
|
||||
ElectronClient/gui/NoteToolbar/NoteToolbar.js
|
||||
ElectronClient/gui/ResourceScreen.js
|
||||
ElectronClient/gui/Root_UpgradeSyncTarget.js
|
||||
ElectronClient/gui/ShareNoteDialog.js
|
||||
ElectronClient/gui/SideBar/commands/focusElementSideBar.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
ReactNativeClient/lib/commands/historyForward.js
|
||||
ReactNativeClient/lib/commands/synchronize.js
|
||||
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
|
||||
ReactNativeClient/lib/hooks/useEffectDebugger.js
|
||||
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
|
||||
ReactNativeClient/lib/hooks/usePrevious.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/media.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/JoplinServerApi.js
|
||||
ReactNativeClient/lib/services/CommandService.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainService.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/KeymapService.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/searchengine/filterParser.js
|
||||
ReactNativeClient/lib/services/searchengine/queryBuilder.js
|
||||
ReactNativeClient/lib/services/SettingUtils.js
|
||||
ReactNativeClient/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
ReactNativeClient/lib/services/synchronizer/LockHandler.js
|
||||
ReactNativeClient/lib/services/synchronizer/MigrationHandler.js
|
||||
ReactNativeClient/lib/services/synchronizer/migrations/1.js
|
||||
ReactNativeClient/lib/services/synchronizer/migrations/2.js
|
||||
ReactNativeClient/lib/services/synchronizer/utils/types.js
|
||||
ReactNativeClient/lib/services/UndoRedoService.js
|
||||
ReactNativeClient/lib/ShareExtension.js
|
||||
ReactNativeClient/lib/shareHandler.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
ReactNativeClient/setUpQuickActions.js
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
124
.eslintrc.js
Normal file
@@ -0,0 +1,124 @@
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'node': true,
|
||||
},
|
||||
'parser': '@typescript-eslint/parser',
|
||||
'extends': ['eslint:recommended'],
|
||||
'settings': {
|
||||
'react': {
|
||||
'version': '16.12',
|
||||
},
|
||||
},
|
||||
'globals': {
|
||||
'Atomics': 'readonly',
|
||||
'SharedArrayBuffer': 'readonly',
|
||||
|
||||
// Jasmine variables
|
||||
'expect': 'readonly',
|
||||
'describe': 'readonly',
|
||||
'it': 'readonly',
|
||||
'beforeAll': 'readonly',
|
||||
'afterAll': 'readonly',
|
||||
'beforeEach': 'readonly',
|
||||
'afterEach': 'readonly',
|
||||
'jasmine': 'readonly',
|
||||
|
||||
// React Native variables
|
||||
'__DEV__': 'readonly',
|
||||
|
||||
// Clipper variables
|
||||
'browserSupportsPromises_': true,
|
||||
'chrome': 'readonly',
|
||||
'browser': 'readonly',
|
||||
|
||||
'tinymce': 'readonly',
|
||||
},
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 2018,
|
||||
'ecmaFeatures': {
|
||||
'jsx': true,
|
||||
},
|
||||
'sourceType': 'module',
|
||||
},
|
||||
'rules': {
|
||||
// -------------------------------
|
||||
// Code correctness
|
||||
// -------------------------------
|
||||
'react/jsx-uses-react': 'error',
|
||||
'react/jsx-uses-vars': 'error',
|
||||
'no-unused-vars': 'error',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'no-constant-condition': 0,
|
||||
'no-prototype-builtins': 0,
|
||||
// This error is always a false positive so far since it detects
|
||||
// possible race conditions in contexts where we know it cannot happen.
|
||||
'require-atomic-updates': 0,
|
||||
'prefer-const': ['error'],
|
||||
'no-var': ['error'],
|
||||
'no-new-func': ['error'],
|
||||
'import/prefer-default-export': ['error'],
|
||||
'import/first': ['error'],
|
||||
'no-array-constructor': ['error'],
|
||||
'radix': ['error'],
|
||||
|
||||
// Warn only for now because fixing everything would take too much
|
||||
// refactoring, but new code should try to stick to it.
|
||||
'complexity': ['warn', { max: 10 }],
|
||||
|
||||
// Checks rules of Hooks
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
// Checks effect dependencies
|
||||
// Disable because of this: https://github.com/facebook/react/issues/16265
|
||||
// "react-hooks/exhaustive-deps": "warn",
|
||||
|
||||
// -------------------------------
|
||||
// Formatting
|
||||
// -------------------------------
|
||||
'space-in-parens': ['error', 'never'],
|
||||
'space-infix-ops': ['error'],
|
||||
'curly': ['error', 'multi-line', 'consistent'],
|
||||
'semi': ['error', 'always'],
|
||||
'eol-last': ['error', 'always'],
|
||||
'quotes': ['error', 'single'],
|
||||
'indent': ['error', 'tab'],
|
||||
'comma-dangle': ['error', {
|
||||
'arrays': 'always-multiline',
|
||||
'objects': 'always-multiline',
|
||||
'imports': 'always-multiline',
|
||||
'exports': 'always-multiline',
|
||||
'functions': 'never',
|
||||
}],
|
||||
'no-trailing-spaces': 'error',
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
'prefer-template': ['error'],
|
||||
'template-curly-spacing': ['error', 'never'],
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
'array-bracket-spacing': ['error', 'never'],
|
||||
'key-spacing': ['error', {
|
||||
'beforeColon': false,
|
||||
'afterColon': true,
|
||||
'mode': 'strict',
|
||||
}],
|
||||
'block-spacing': ['error'],
|
||||
'brace-style': ['error', '1tbs', { 'allowSingleLine': true }],
|
||||
'no-spaced-func': ['error'],
|
||||
'func-call-spacing': ['error'],
|
||||
'space-before-function-paren': ['error', {
|
||||
'anonymous': 'never',
|
||||
'named': 'never',
|
||||
'asyncArrow': 'always',
|
||||
}],
|
||||
'multiline-comment-style': ['error', 'separate-lines'],
|
||||
'space-before-blocks': 'error',
|
||||
'spaced-comment': ['error', 'always'],
|
||||
'keyword-spacing': ['error', { 'before': true, 'after': true }],
|
||||
},
|
||||
'plugins': [
|
||||
'react',
|
||||
'@typescript-eslint',
|
||||
'react-hooks',
|
||||
'import',
|
||||
],
|
||||
};
|
5
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: joplin
|
||||
github: laurent22
|
||||
custom: https://joplinapp.org/donate/
|
9
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
👉 Please follow one of these issue templates:
|
||||
- https://github.com/laurent22/joplin/issues/new/choose
|
||||
|
||||
⚠️
|
||||
The GitHub issue tracker is for **bugs** and **security issues** ONLY. For feature requests and support, please use the forum:
|
||||
https://discourse.joplinapp.org/
|
||||
⚠️
|
||||
|
||||
Note: to keep the backlog clean and actionable, issues may be immediately closed if they do not follow one of the above issue templates.
|
15
.github/ISSUE_TEMPLATE/---feature-requests-and-support.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: "\U0001F914 Feature requests and support"
|
||||
about: 'For non-bug issues we recommend using the forum, where you''ll be more likely
|
||||
to get an answer: https://discourse.joplinapp.org/'
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
If this is a feature request or a support query, please note that you'll not get an answer here.
|
||||
|
||||
Instead we recommend using the forum where you'll are a lot more likely to get an answer: https://discourse.joplinapp.org/
|
||||
|
||||
The forum is also the right place to submit a feature request so that it can be discussed by other users.
|
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: "\U0001F41B Bug Report"
|
||||
about: Report a reproducible bug or regression in Joplin.
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Please provide a clear and concise description of what the bug is. (In the section Steps To Reproduce.)
|
||||
Include screenshots if needed.
|
||||
Please test using the latest Joplin release to make sure your issue has not already been fixed.
|
||||
-->
|
||||
|
||||
<!--
|
||||
IMPORTANT: If you are reporting a clipper bug, please include an example URL that shows the issue.
|
||||
Without the URL the issue is likely to be closed.
|
||||
-->
|
||||
|
||||
## Environment
|
||||
|
||||
Joplin version:
|
||||
Platform:
|
||||
OS specifics:
|
||||
|
||||
<!--
|
||||
Platform can be one of: macOS, Linux, Windows, Android, iOS, terminal (or a combination)
|
||||
OS specifics: e.g. OS version, Linux distribution, Android/iOS version...
|
||||
-->
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
<!--
|
||||
Issues without reproduction steps are likely to stall.
|
||||
-->
|
||||
|
||||
## Describe what you expected to happen
|
||||
|
||||
|
||||
|
||||
## Logfile
|
||||
|
||||
<!--
|
||||
Please attach a debug log. Issues without a debug log are likely to stall.
|
||||
For information on how to collect a log file: https://joplinapp.org/debugging/
|
||||
-->
|
25
.github/PULL_REQUEST_TEMPLATE
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
|
||||
Please prefix the title with the platform you are targetting:
|
||||
|
||||
Here are some examples of good titles:
|
||||
|
||||
- Desktop: Resolves #123: Added new setting to change font
|
||||
- Mobile, Desktop: Fixes #456: Fixed config screen error
|
||||
- All: Resolves #777: Made synchronisation faster
|
||||
|
||||
And here's an explanation of the title format:
|
||||
|
||||
- "Desktop" for the Windows/macOS/Linux app (Electron app)
|
||||
- "Mobile" for the mobile app (or "Android" / "iOS" if the pull request only applies to one of the mobile platforms)
|
||||
- "CLI" for the CLI app
|
||||
|
||||
If it's two platforms, separate them with commas - "Desktop, Mobile" or if it's for all platforms, prefix with "All".
|
||||
|
||||
If it's not related to any platform (such as a translation, change to the documentation, etc.), simply don't add a platform.
|
||||
|
||||
Then please append the issue that you've addressed or fixed. Use "Resolves #123" for new features or improvements and "Fixes #123" for bug fixes.
|
||||
|
||||
AND PLEASE READ THE GUIDE: https://github.com/laurent22/joplin/blob/master/CONTRIBUTING.md
|
||||
|
||||
-->
|
25
.github/lock.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 7
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
||||
skipCreatedBefore: false
|
||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: false
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: false
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - help-wanted
|
||||
# lockLabel: outdated
|
||||
# pulls:
|
||||
# daysUntilLock: 30
|
||||
# Repository to extend settings from
|
||||
# _extends: repo
|
23
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 30
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- "good first issue"
|
||||
- "upstream"
|
||||
- "backlog"
|
||||
- "high"
|
||||
- "spec"
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs.
|
||||
You may comment on the issue and I will leave it open.
|
||||
Thank you for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.
|
||||
only: issues
|
135
.gitignore
vendored
@@ -37,8 +37,137 @@ _mydocs
|
||||
Assets/DownloadBadges*.psd
|
||||
node_modules
|
||||
Tools/github_oauth_token.txt
|
||||
CliClient/tests/support/amazon-s3-auth.json
|
||||
_releases
|
||||
ReactNativeClient/lib/csstojs/
|
||||
ElectronClient/app/gui/note-viewer/fonts/
|
||||
ElectronClient/app/gui/note-viewer/lib.js
|
||||
Tools/commit_hook.txt
|
||||
ReactNativeClient/lib/rnInjectedJs/
|
||||
ElectronClient/gui/note-viewer/fonts/
|
||||
ElectronClient/gui/note-viewer/lib.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/supportedLocales.js
|
||||
Clipper-source/
|
||||
Clipper/joplin-webclipper-source.zip
|
||||
joplin-webclipper-source.zip
|
||||
Tools/commit_hook.txt
|
||||
.vscode/*
|
||||
*.map
|
||||
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
CliClient/app/LinkSelector.js
|
||||
CliClient/build/LinkSelector.js
|
||||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.js
|
||||
ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/ErrorBoundary.js
|
||||
ElectronClient/gui/Header/commands/focusSearch.js
|
||||
ElectronClient/gui/MainScreen/commands/editAlarm.js
|
||||
ElectronClient/gui/MainScreen/commands/exportPdf.js
|
||||
ElectronClient/gui/MainScreen/commands/hideModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/moveToFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newNote.js
|
||||
ElectronClient/gui/MainScreen/commands/newNotebook.js
|
||||
ElectronClient/gui/MainScreen/commands/newTodo.js
|
||||
ElectronClient/gui/MainScreen/commands/print.js
|
||||
ElectronClient/gui/MainScreen/commands/renameFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/renameTag.js
|
||||
ElectronClient/gui/MainScreen/commands/search.js
|
||||
ElectronClient/gui/MainScreen/commands/selectTemplate.js
|
||||
ElectronClient/gui/MainScreen/commands/setTags.js
|
||||
ElectronClient/gui/MainScreen/commands/showModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
ElectronClient/gui/NoteEditor/commands/focusElementNoteBody.js
|
||||
ElectronClient/gui/NoteEditor/commands/focusElementNoteTitle.js
|
||||
ElectronClient/gui/NoteEditor/commands/showLocalSearch.js
|
||||
ElectronClient/gui/NoteEditor/commands/showRevisions.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/AceEditor.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
ElectronClient/gui/NoteEditor/NoteEditor.js
|
||||
ElectronClient/gui/NoteEditor/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/contextMenu.js
|
||||
ElectronClient/gui/NoteEditor/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/resourceHandling.js
|
||||
ElectronClient/gui/NoteEditor/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/utils/useDropHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useFormNote.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
|
||||
ElectronClient/gui/NoteListItem.js
|
||||
ElectronClient/gui/NoteToolbar/NoteToolbar.js
|
||||
ElectronClient/gui/ResourceScreen.js
|
||||
ElectronClient/gui/Root_UpgradeSyncTarget.js
|
||||
ElectronClient/gui/ShareNoteDialog.js
|
||||
ElectronClient/gui/SideBar/commands/focusElementSideBar.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
ReactNativeClient/lib/commands/historyForward.js
|
||||
ReactNativeClient/lib/commands/synchronize.js
|
||||
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
|
||||
ReactNativeClient/lib/hooks/useEffectDebugger.js
|
||||
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
|
||||
ReactNativeClient/lib/hooks/usePrevious.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/media.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/JoplinServerApi.js
|
||||
ReactNativeClient/lib/services/CommandService.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainService.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/KeymapService.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/searchengine/filterParser.js
|
||||
ReactNativeClient/lib/services/searchengine/queryBuilder.js
|
||||
ReactNativeClient/lib/services/SettingUtils.js
|
||||
ReactNativeClient/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
ReactNativeClient/lib/services/synchronizer/LockHandler.js
|
||||
ReactNativeClient/lib/services/synchronizer/MigrationHandler.js
|
||||
ReactNativeClient/lib/services/synchronizer/migrations/1.js
|
||||
ReactNativeClient/lib/services/synchronizer/migrations/2.js
|
||||
ReactNativeClient/lib/services/synchronizer/utils/types.js
|
||||
ReactNativeClient/lib/services/UndoRedoService.js
|
||||
ReactNativeClient/lib/ShareExtension.js
|
||||
ReactNativeClient/lib/shareHandler.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
ReactNativeClient/setUpQuickActions.js
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
|
67
.travis.yml
@@ -1,5 +1,5 @@
|
||||
# Only build tags (Doesn't work - doesn't build anything)
|
||||
if: tag IS present
|
||||
if: tag IS present OR type = pull_request
|
||||
|
||||
rvm: 2.3.3
|
||||
|
||||
@@ -9,6 +9,7 @@ rvm: 2.3.3
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- dev
|
||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
|
||||
matrix:
|
||||
@@ -16,7 +17,7 @@ matrix:
|
||||
- os: osx
|
||||
osx_image: xcode9.0
|
||||
language: node_js
|
||||
node_js: "8"
|
||||
node_js: "10"
|
||||
env:
|
||||
- ELECTRON_CACHE=$HOME/.cache/electron
|
||||
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
|
||||
@@ -25,7 +26,7 @@ matrix:
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: node_js
|
||||
node_js: "8"
|
||||
node_js: "10"
|
||||
env:
|
||||
- ELECTRON_CACHE=$HOME/.cache/electron
|
||||
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
|
||||
@@ -42,6 +43,8 @@ before_install:
|
||||
|
||||
# Silence apt-get update errors (for example when a module doesn't exist) since
|
||||
# otherwise it will make the whole build fails, even though all we need is yarn.
|
||||
|
||||
# libsecret-1-dev is required for keytar - https://github.com/atom/node-keytar
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install yarn
|
||||
@@ -50,12 +53,64 @@ before_install:
|
||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y yarn
|
||||
sudo apt-get install -y gettext
|
||||
sudo apt-get install -y libsecret-1-dev
|
||||
fi
|
||||
|
||||
script:
|
||||
- |
|
||||
# Install tools
|
||||
npm install
|
||||
cd Tools
|
||||
npm install
|
||||
cd ../ElectronClient/app
|
||||
rsync -aP --delete ../../ReactNativeClient/lib/ lib/
|
||||
npm install && yarn dist
|
||||
cd ..
|
||||
|
||||
# Run test units.
|
||||
# Only do it for pull requests because Travis randomly fails to run them
|
||||
# and that would break the desktop release.
|
||||
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
|
||||
cd CliClient
|
||||
npm run test-ci
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
cd ..
|
||||
fi
|
||||
|
||||
# Run linter for pull requests only - this is so that
|
||||
# bypassing eslint is allowed for urgent fixes.
|
||||
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
|
||||
npm run linter-ci ./
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
|
||||
# Validate translations - this is needed as some users manually
|
||||
# edit .po files (and often make mistakes) instead of using a proper
|
||||
# tool like poedit. Doing it for Linux only is sufficient.
|
||||
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
|
||||
if [ "$TRAVIS_OS_NAME" != "osx" ]; then
|
||||
node Tools/validate-translation.js
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Find out if we should run the build or not. Electron-builder gets stuck when
|
||||
# builing PRs so we disable it in this case. The Linux build should provide
|
||||
# enough info if the app builds or not.
|
||||
# https://github.com/electron-userland/electron-builder/issues/4263
|
||||
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Prepare the Electron app and build it
|
||||
cd ElectronClient
|
||||
USE_HARD_LINKS=false npm run dist
|
||||
|
Before Width: | Height: | Size: 986 B |
BIN
Assets/AdresseSupport.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
Assets/AdresseTranslation.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
Assets/GitHubSponsorIcon.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
Assets/GooglePlayStoreIcon.psd
Normal file
BIN
Assets/IconBackgroundSquare.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
Assets/ImageSources/Joplin.ico
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
Assets/ImageSources/RoundedCornersMac_1024x1024.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
Assets/ImageSources/RoundedCorners_1024x1024.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
Assets/ImageSources/RoundedCorners_16x16.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Assets/ImageSources/RoundedCorners_64x64.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
Assets/ImageSources/Square_1024x1024.png
Normal file
After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 38 KiB |
19
Assets/JoplinIcon.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 682.66669 682.66669" height="682.66669" width="682.66669" xml:space="preserve" id="svg2" version="1.1">
|
||||
<defs id="defs6">
|
||||
<linearGradient id="linearGradient26" spreadMethod="pad" gradientTransform="matrix(-4387.91,4387.91,4387.91,4387.91,4753.95,366.05)" gradientUnits="userSpaceOnUse" y2="0" x2="1" y1="0" x1="0">
|
||||
<stop id="stop22" offset="0" style="stop-opacity:1;stop-color:#004caf"/>
|
||||
<stop id="stop24" offset="1" style="stop-opacity:1;stop-color:#1f95f8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="matrix(1.3333333,0,0,-1.3333333,0,682.66667)" id="g10">
|
||||
<g transform="scale(0.1)" id="g12">
|
||||
<g id="g14">
|
||||
<g clip-path="url(#clipPath20)" id="g16">
|
||||
<path id="path28" style="fill:url(#linearGradient26);fill-opacity:1;fill-rule:nonzero;stroke:none" d="M 3873.89,0 H 1246.11 C 560.754,0 0,560.75 0,1246.11 V 3873.88 C 0,4559.25 560.754,5120 1246.11,5120 H 3873.89 C 4559.25,5120 5120,4559.25 5120,3873.88 V 1246.11 C 5120,560.75 4559.25,0 3873.89,0"/>
|
||||
</g>
|
||||
</g>
|
||||
<path id="path30" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" d="M 3961.59,4435.23 H 2570.18 c -13.15,0 -23.78,-10.64 -23.78,-23.77 v -441.84 c 0,-14.87 12.04,-26.92 26.92,-26.92 h 190.77 c 77.16,0 139.73,-59.35 146.43,-134.77 V 3505 3336.23 1728.75 1717.36 h -0.05 c 0.48,-16.84 -0.19,-33.4 -1.83,-49.71 -0.18,-2.38 -0.5,-4.73 -0.79,-7.09 -1.1,-9.53 -2.32,-19.01 -4.17,-28.29 -1.01,-5.29 -2.44,-10.44 -3.71,-15.65 -1.71,-6.93 -3.09,-13.97 -5.22,-20.75 -12.58,-40.27 -32.47,-77.62 -59.98,-110.5 -1.01,-1.17 -2.26,-2.25 -3.26,-3.41 -8.39,-9.72 -17.2,-19.19 -26.95,-28.06 -9.84,-8.95 -20.26,-17.27 -31.21,-25 -77.84,-55.14 -182.61,-79.4 -299.67,-68.2 -149.26,14.03 -297.34,81.72 -417.03,190.62 -119.67,108.89 -194.08,243.62 -209.48,379.41 -13.85,121.48 22.55,228.38 102.42,301.05 0.21,0.16 0.4,0.31 0.56,0.48 3.09,2.77 6.49,5.2 9.67,7.87 57.16,47.89 131.67,76.91 216.7,84.91 0.96,0.09 1.88,0.24 2.79,0.32 8.95,0.79 18.07,1.15 27.27,1.49 4.81,0.16 9.56,0.5 14.44,0.54 1.62,0.02 3.16,0.19 4.78,0.19 2.9,0 5.91,-0.38 8.81,-0.42 13.4,-0.21 26.9,-0.76 40.67,-1.94 1.74,-0.14 3.4,-0.08 5.19,-0.24 1.27,-0.13 2.53,-0.41 3.8,-0.54 78,-7.82 155.23,-31.11 228.52,-66.4 1.53,-0.07 3.3,-0.54 5.51,-1.76 22.34,-12.34 26.62,0.9 27.28,9.65 v 382.24 282.82 c 0,19.05 -13.25,35.9 -31.83,39.99 -394.76,86.88 -782.08,-3.55 -1055.38,-252.34 -238.75,-217.18 -354.24,-530.58 -316.82,-859.79 33.39,-293.23 183.91,-574.94 423.88,-793.33 233.89,-212.79 531.69,-345.86 838.88,-374.801 42.33,-3.918 84.86,-5.938 126.36,-5.938 293.38,0 565.61,100.598 766.54,283.379 190.34,173.3 304.35,411.27 321.08,670.16 l 1.55,1697.91 h 0.17 v 453.97 h 0.06 v 7.92 c 1.72,80.12 67.05,144.58 147.61,144.58 h 190.77 c 14.86,0 26.92,12.05 26.92,26.92 v 441.84 c 0,13.13 -10.63,23.77 -23.78,23.77"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
31
Assets/JoplinIconBlack.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg40"
|
||||
version="1.1"
|
||||
width="1536"
|
||||
height="1536"
|
||||
viewBox="0 0 1536 1536">
|
||||
<metadata
|
||||
id="metadata46">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs44" />
|
||||
<path
|
||||
id="path38"
|
||||
fill="#ffffff"
|
||||
d="M 373.834,0 C 168.227,0 0,168.223 0,373.834 V 1162.17 C 0,1367.778 168.227,1536 373.834,1536 H 1162.17 C 1367.778,1536 1536,1367.778 1536,1162.17 V 373.834 C 1536,168.224 1367.778,0 1162.17,0 Z m 397.222,205.431 h 417.424 a 7.132,7.132 0 0 1 7.132,7.133 v 132.552 c 0,4.461 -3.619,8.073 -8.077,8.073 h -57.23 c -24.168,0 -43.768,19.338 -44.284,43.374 v 2.377 h -0.017 v 136.191 h -0.053 l -0.466,509.375 c -5.02,77.667 -39.222,149.056 -96.324,201.046 -60.28,54.834 -141.948,85.017 -229.962,85.017 -12.45,0 -25.208,-0.61 -37.907,-1.785 -92.157,-8.682 -181.494,-48.601 -251.662,-112.438 -71.99,-65.517 -117.147,-150.03 -127.164,-238 -11.226,-98.763 23.42,-192.783 95.045,-257.937 81.99,-74.637 198.185,-101.768 316.613,-75.704 5.574,1.227 9.55,6.282 9.55,11.997 v 199.52 c -0.199,2.625 -1.481,6.599 -8.183,2.896 -0.663,-0.365 -1.194,-0.511 -1.653,-0.531 -21.987,-10.587 -45.159,-17.57 -68.559,-19.916 -0.38,-0.04 -0.757,-0.124 -1.138,-0.163 -0.537,-0.048 -1.034,-0.033 -1.556,-0.075 -4.13,-0.354 -8.183,-0.517 -12.203,-0.58 -0.87,-0.011 -1.771,-0.127 -2.641,-0.127 -0.486,0 -0.951,0.05 -1.437,0.057 -1.464,0.011 -2.886,0.115 -4.33,0.163 -2.76,0.102 -5.497,0.211 -8.182,0.448 -0.273,0.024 -0.547,0.07 -0.835,0.097 -25.509,2.4 -47.864,11.104 -65.012,25.47 -0.954,0.802 -1.974,1.53 -2.9,2.36 a 1.34,1.34 0 0 1 -0.168,0.146 c -23.96,21.8 -34.881,53.872 -30.726,90.316 4.62,40.737 26.94,81.156 62.841,113.823 35.908,32.67 80.335,52.977 125.113,57.186 35.118,3.36 66.547,-3.919 89.899,-20.461 a 97.255,97.255 0 0 0 9.365,-7.501 c 2.925,-2.661 5.569,-5.5 8.086,-8.416 0.3,-0.348 0.672,-0.673 0.975,-1.024 8.253,-9.864 14.222,-21.067 17.996,-33.148 0.639,-2.034 1.051,-4.148 1.564,-6.227 0.381,-1.563 0.81,-3.106 1.112,-4.693 0.555,-2.784 0.923,-5.632 1.253,-8.49 0.086,-0.709 0.183,-1.414 0.237,-2.128 0.492,-4.893 0.693,-9.858 0.55,-14.91 h 0.013 V 393.623 c -2.01,-22.626 -20.78,-40.434 -43.928,-40.434 h -57.23 a 8.071,8.071 0 0 1 -8.077,-8.073 V 212.564 a 7.132,7.132 0 0 1 7.136,-7.133 z" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
96
Assets/JoplinLetter.eps
Normal file
@@ -0,0 +1,96 @@
|
||||
%!PS-Adobe-3.0 EPSF-3.0
|
||||
%%Creator: cairo 1.15.6 (http://cairographics.org)
|
||||
%%CreationDate: Wed Dec 04 10:22:47 2019
|
||||
%%Pages: 1
|
||||
%%DocumentData: Clean7Bit
|
||||
%%LanguageLevel: 2
|
||||
%%BoundingBox: 0 0 331 372
|
||||
%%EndComments
|
||||
%%BeginProlog
|
||||
save
|
||||
50 dict begin
|
||||
/q { gsave } bind def
|
||||
/Q { grestore } bind def
|
||||
/cm { 6 array astore concat } bind def
|
||||
/w { setlinewidth } bind def
|
||||
/J { setlinecap } bind def
|
||||
/j { setlinejoin } bind def
|
||||
/M { setmiterlimit } bind def
|
||||
/d { setdash } bind def
|
||||
/m { moveto } bind def
|
||||
/l { lineto } bind def
|
||||
/c { curveto } bind def
|
||||
/h { closepath } bind def
|
||||
/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
|
||||
0 exch rlineto 0 rlineto closepath } bind def
|
||||
/S { stroke } bind def
|
||||
/f { fill } bind def
|
||||
/f* { eofill } bind def
|
||||
/n { newpath } bind def
|
||||
/W { clip } bind def
|
||||
/W* { eoclip } bind def
|
||||
/BT { } bind def
|
||||
/ET { } bind def
|
||||
/pdfmark where { pop globaldict /?pdfmark /exec load put }
|
||||
{ globaldict begin /?pdfmark /pop load def /pdfmark
|
||||
/cleartomark load def end } ifelse
|
||||
/BDC { mark 3 1 roll /BDC pdfmark } bind def
|
||||
/EMC { mark /EMC pdfmark } bind def
|
||||
/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
|
||||
/Tj { show currentpoint cairo_store_point } bind def
|
||||
/TJ {
|
||||
{
|
||||
dup
|
||||
type /stringtype eq
|
||||
{ show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
|
||||
} forall
|
||||
currentpoint cairo_store_point
|
||||
} bind def
|
||||
/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
|
||||
cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
|
||||
/Tf { pop /cairo_font exch def /cairo_font_matrix where
|
||||
{ pop cairo_selectfont } if } bind def
|
||||
/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
|
||||
/cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
|
||||
/cairo_font where { pop cairo_selectfont } if } bind def
|
||||
/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
|
||||
cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
|
||||
/g { setgray } bind def
|
||||
/rg { setrgbcolor } bind def
|
||||
/d1 { setcachedevice } bind def
|
||||
/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def
|
||||
/cairo_image { image cairo_flush_ascii85_file } def
|
||||
/cairo_imagemask { imagemask cairo_flush_ascii85_file } def
|
||||
%%EndProlog
|
||||
%%BeginSetup
|
||||
%%EndSetup
|
||||
%%Page: 1 1
|
||||
%%BeginPageSetup
|
||||
%%PageBoundingBox: 0 0 331 372
|
||||
%%EndPageSetup
|
||||
q 0 0 331 372 rectclip
|
||||
1 0 0 -1 0 372 cm q
|
||||
0 g
|
||||
116.223 371.219 m 92.629 368.047 79.551 364.387 63.867 356.57 c 34.902
|
||||
342.121 12.555 317.016 3.848 289.133 c 0.383 278.039 -0.012 275.32 0 262.508
|
||||
c 0.008 251.938 0.258 249.27 1.699 244.234 c 4.723 233.676 8.77 226.742
|
||||
16.426 219.008 c 25.66 209.676 35.293 205.477 48.723 204.93 c 61.566 204.41
|
||||
70.113 206.629 78.211 212.594 c 83.371 216.391 88.637 223.828 91.578 231.484
|
||||
c 93.445 236.34 93.59 237.676 94.133 255.008 c 94.699 273.145 95.164 276.641
|
||||
98.078 284.684 c 102.059 295.672 108.82 303.285 118.855 308.086 c 127.93
|
||||
312.426 134.84 313.762 146.094 313.348 c 153.41 313.078 156.309 312.625
|
||||
160.699 311.07 c 173.777 306.434 184.691 296.008 189.934 283.133 c 195.355
|
||||
269.828 195.344 270.066 195.328 160.883 c 195.313 65.117 195.297 64.098
|
||||
193.781 60.758 c 190.078 52.586 186.18 51.125 166.371 50.488 c 151.848
|
||||
50.02 l 151.848 0 l 329.973 0.383 l 330.168 25.199 l 330.367 50.02 l 315.918
|
||||
50.527 l 307.902 50.809 300.027 51.465 298.23 52.004 c 293.328 53.461 290.211
|
||||
56.363 288.234 61.305 c 286.504 65.633 l 286.027 164.258 l 285.547 264.215
|
||||
285.48 266.555 282.668 280.234 c 277.531 305.25 259.137 330.953 234.594
|
||||
347.406 c 214.891 360.621 193.523 367.852 164.223 371.227 c 156.445 372.121
|
||||
122.898 372.117 116.223 371.219 c h
|
||||
116.223 371.219 m f
|
||||
Q Q
|
||||
showpage
|
||||
%%Trailer
|
||||
end restore
|
||||
%%EOF
|
BIN
Assets/JoplinLetter.png
Normal file
After Width: | Height: | Size: 11 KiB |
@@ -1,6 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
@@ -9,54 +7,63 @@
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="116.54575mm"
|
||||
height="131.19589mm"
|
||||
viewBox="0 0 116.54575 131.19589"
|
||||
viewBox="0 0 682.66669 682.66669"
|
||||
height="682.66669"
|
||||
width="682.66669"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||
sodipodi:docname="JoplinLetter.svg">
|
||||
sodipodi:docname="JoplinLetter.svg"
|
||||
inkscape:version="1.0beta1 (32d4812, 2019-09-19)"><metadata
|
||||
id="metadata23">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
|
||||
<dc:title></dc:title>
|
||||
|
||||
</cc:Work>
|
||||
|
||||
</rdf:RDF>
|
||||
|
||||
</metadata>
|
||||
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
inkscape:document-rotation="0"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="907"
|
||||
id="namedview21"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:cx="400"
|
||||
inkscape:cy="560"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
|
||||
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.49497475"
|
||||
inkscape:cx="152.11122"
|
||||
inkscape:cy="-26.090631"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-2.7903623,-2.175533)">
|
||||
<path
|
||||
style="fill:#000000;stroke-width:0.26458332"
|
||||
d="m 43.790458,133.13317 c -8.32317,-1.11843 -12.937,-2.40956 -18.46857,-5.16822 -10.21924,-5.09644 -18.1023498,-13.95338 -21.1745998,-23.79038 -1.22214,-3.91319 -1.3607,-4.872332 -1.35685,-9.392712 0.003,-3.72804 0.0907,-4.66941 0.59927,-6.44569 1.0664,-3.7246 2.49409,-6.1704 5.19529,-8.90014 3.2574198,-3.29184 6.6565798,-4.77332 11.3929598,-4.96548 4.53189,-0.18388 7.54661,0.59927 10.40386,2.70266 1.82035,1.34007 3.67693,3.96421 4.71565,6.66525 0.65839,1.71204 0.70959,2.1839 0.90042,8.29756 0.19973,6.39855 0.36372,7.6318 1.39223,10.469902 1.40468,3.87611 3.78939,6.56189 7.33039,8.25588 3.20047,1.53108 5.63801,2.00183 9.60817,1.8556 2.58182,-0.0951 3.60332,-0.25442 5.15337,-0.80371 4.61358,-1.63493 8.46322,-5.31381 10.31326,-9.85579 1.91154,-4.693002 1.90785,-4.609372 1.90213,-43.127082 -0.005,-33.78395 -0.0106,-34.14337 -0.54484,-35.32188 -1.30698,-2.882895 -2.68223,-3.398165 -9.66971,-3.622945 l -5.12472,-0.16486 V 10.998334 2.175533 l 31.41927,0.06723 31.419272,0.06723 0.0697,8.755726 0.0697,8.755724 -5.09675,0.1793 c -2.82759,0.0995 -5.60596,0.33101 -6.24051,0.52006 -1.72896,0.5151 -2.82899,1.538795 -3.52569,3.281045 l -0.61059,1.5269 -0.16762,34.7927 c -0.16988,35.26321 -0.19381,36.08914 -1.18496,40.914372 -1.81292,8.82581 -8.301582,17.89221 -16.959672,23.69719 -6.95182,4.66099 -14.48972,7.21214 -24.82645,8.40235 -2.7431,0.31585 -14.57797,0.31433 -16.93333,-0.002 z"
|
||||
id="path21"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
id="defs6" />
|
||||
|
||||
|
||||
<path
|
||||
id="path30"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.133333;stop-opacity:1"
|
||||
d="M 528.21199,91.302685 H 342.69066 c -1.75334,0 -3.17067,1.418666 -3.17067,3.169333 v 58.912002 c 0,1.98266 1.60533,3.58933 3.58933,3.58933 h 25.436 c 10.288,0 18.63067,7.91333 19.524,17.96933 v 40.39067 22.50266 214.33067 1.51866 h -0.007 c 0.064,2.24534 -0.0253,4.45334 -0.244,6.628 -0.024,0.31734 -0.0667,0.63067 -0.10534,0.94534 -0.14666,1.27066 -0.30933,2.53466 -0.556,3.772 -0.13466,0.70533 -0.32533,1.392 -0.49466,2.08666 -0.228,0.924 -0.412,1.86267 -0.696,2.76667 -1.67734,5.36933 -4.32934,10.34933 -7.99734,14.73333 -0.13466,0.156 -0.30133,0.3 -0.43466,0.45467 -1.11867,1.296 -2.29334,2.55866 -3.59334,3.74133 -1.312,1.19333 -2.70133,2.30267 -4.16133,3.33333 -10.37867,7.352 -24.348,10.58667 -39.956,9.09334 -19.90133,-1.87067 -39.64533,-10.896 -55.604,-25.416 -15.956,-14.51867 -25.87733,-32.48267 -27.93066,-50.588 -1.84667,-16.19733 3.00666,-30.45067 13.656,-40.14 0.028,-0.0213 0.0533,-0.0413 0.0747,-0.064 0.412,-0.36933 0.86534,-0.69333 1.28934,-1.04933 7.62133,-6.38534 17.556,-10.25467 28.89333,-11.32134 0.128,-0.012 0.25067,-0.032 0.372,-0.0427 1.19333,-0.10534 2.40933,-0.15334 3.636,-0.19867 0.64133,-0.0213 1.27467,-0.0667 1.92533,-0.072 0.216,-0.003 0.42134,-0.0253 0.63734,-0.0253 0.38666,0 0.788,0.0507 1.17466,0.056 1.78667,0.028 3.58667,0.10133 5.42267,0.25866 0.232,0.0187 0.45333,0.0107 0.692,0.032 0.16933,0.0173 0.33733,0.0547 0.50667,0.072 10.4,1.04267 20.69733,4.148 30.46933,8.85334 0.204,0.009 0.44,0.072 0.73466,0.23466 2.97867,1.64534 3.54934,-0.12 3.63734,-1.28666 V 329.57734 291.868 c 0,-2.54 -1.76667,-4.78666 -4.244,-5.332 -52.63467,-11.584 -104.27733,0.47334 -140.71733,33.64534 -31.83333,28.95733 -47.232,70.74399 -42.24267,114.63866 4.452,39.09733 24.52134,76.65866 56.51733,105.77733 31.18534,28.372 70.892,46.11467 111.85067,49.97347 5.644,0.5224 11.31467,0.79173 16.848,0.79173 39.11733,0 75.41466,-13.41307 102.20533,-37.78387 25.37867,-23.10666 40.58,-54.836 42.81066,-89.35466 l 0.20667,-226.388 h 0.0227 v -60.52933 h 0.008 v -1.056 c 0.22933,-10.68266 8.94,-19.27733 19.68133,-19.27733 h 25.436 c 1.98133,0 3.58933,-1.60667 3.58933,-3.58933 V 94.472018 c 0,-1.750667 -1.41733,-3.169333 -3.17066,-3.169333"
|
||||
inkscape:connector-curvature="0" />
|
||||
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 679 B After Width: | Height: | Size: 697 B |
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 335 KiB After Width: | Height: | Size: 336 KiB |
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 244 KiB |
BIN
Assets/Screenshots/iOS/Screenshot_iPhone_Portrait_X.png
Normal file
After Width: | Height: | Size: 249 KiB |
BIN
Assets/Screenshots/iOS/Screenshot_iPhone_Portrait_X.psd
Normal file
109
Assets/SmallTile.svg
Normal file
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
sodipodi:docname="disegno.svg"
|
||||
inkscape:version="1.0rc1 (09960d6, 2020-04-09)"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 70 70"
|
||||
height="70"
|
||||
width="70">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-height="775"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:guide-bbox="true"
|
||||
showguides="true"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:showpageshadow="false"
|
||||
showborder="true"
|
||||
showgrid="false"
|
||||
inkscape:document-rotation="0"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-units="px"
|
||||
inkscape:cy="39.253064"
|
||||
inkscape:cx="25.246811"
|
||||
inkscape:zoom="5.8562241"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base"
|
||||
units="px">
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide1932"
|
||||
orientation="-1,0"
|
||||
position="12,70" />
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide1934"
|
||||
orientation="-1,0"
|
||||
position="58,70" />
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide1936"
|
||||
orientation="0,1"
|
||||
position="0,58" />
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide1938"
|
||||
orientation="0,1"
|
||||
position="0,12" />
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide2021"
|
||||
orientation="-1,0"
|
||||
position="35,70" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Livello 1">
|
||||
<path
|
||||
inkscape:transform-center-y="23.027731"
|
||||
inkscape:transform-center-x="-17.510929"
|
||||
id="path30"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.133334;stop-opacity:1"
|
||||
d="M 52.1731,12 H 35.107236 c -0.161294,0 -0.291666,0.130494 -0.291666,0.291543 v 5.419239 c 0,0.182381 0.147678,0.330176 0.330178,0.330176 h 2.339824 c 0.94638,0 1.713811,0.727936 1.795988,1.652976 v 3.715485 2.069989 19.715998 0.139708 h -6.44e-4 c 0.0058,0.206545 -0.0023,0.409658 -0.02244,0.609702 -0.0023,0.02918 -0.0062,0.05801 -0.0097,0.08697 -0.01349,0.116897 -0.02846,0.233161 -0.05114,0.346981 -0.01243,0.06489 -0.02993,0.128044 -0.0455,0.191949 -0.02097,0.08499 -0.0379,0.171342 -0.06401,0.254505 -0.1543,0.493916 -0.39825,0.95202 -0.735666,1.355297 -0.01243,0.01438 -0.02771,0.02761 -0.03998,0.04182 -0.102908,0.119222 -0.210962,0.235367 -0.330548,0.34416 -0.120695,0.109778 -0.248492,0.211819 -0.382795,0.306628 -0.954719,0.676302 -2.23974,0.973854 -3.6755,0.836485 -1.830697,-0.172088 -3.646921,-1.002309 -5.114939,-2.337984 -1.467772,-1.335554 -2.380422,-2.988039 -2.569305,-4.653525 -0.169868,-1.489971 0.276578,-2.801116 1.256197,-3.692426 0.0027,-0.0019 0.005,-0.0037 0.0069,-0.0058 0.0379,-0.03398 0.0796,-0.06379 0.1186,-0.09654 0.701076,-0.58738 1.614951,-0.943313 2.657858,-1.041435 0.01172,-0.0011 0.02306,-0.003 0.03423,-0.0039 0.109777,-0.0097 0.221631,-0.01402 0.334471,-0.01827 0.05899,-0.0019 0.117251,-0.0062 0.177111,-0.0065 0.01986,-2.76e-4 0.03875,-0.0023 0.05864,-0.0023 0.03557,0 0.07248,0.0046 0.108055,0.0051 0.164347,0.0027 0.329933,0.0094 0.498825,0.02379 0.02134,0.0017 0.0417,9.78e-4 0.06365,0.003 0.01562,0.0016 0.03103,0.005 0.04662,0.0065 0.956682,0.09592 1.90392,0.381568 2.802834,0.814406 0.01876,8.39e-4 0.04047,0.0065 0.06759,0.02159 0.274004,0.151353 0.326498,-0.01101 0.334593,-0.118351 v -4.688237 -3.46883 c 0,-0.233653 -0.162519,-0.440321 -0.3904,-0.490484 -4.841795,-1.065597 -9.592336,0.04355 -12.944402,3.09499 -2.928307,2.663747 -4.344811,6.507647 -3.885849,10.545461 0.409535,3.596512 2.255685,7.05173 5.198956,9.730318 2.868698,2.609903 6.521262,4.242028 10.288996,4.596993 0.519184,0.04805 1.040822,0.07283 1.549826,0.07283 3.598351,0 6.937296,-1.233851 9.401735,-3.475688 2.33455,-2.125552 3.732902,-5.044294 3.938097,-8.219619 l 0.01901,-20.825138 h 0.0021 V 19.91155 h 7.7e-4 v -0.09714 c 0.02109,-0.982682 0.822378,-1.773296 1.81046,-1.773296 h 2.339824 c 0.182259,0 0.330177,-0.147802 0.330177,-0.330177 v -5.419299 c -2.1e-5,-0.161046 -0.130406,-0.291543 -0.291686,-0.291543"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<inkscape:templateinfo>
|
||||
<inkscape:name>SmallTile</inkscape:name>
|
||||
<inkscape:shortdesc>Small tile</inkscape:shortdesc>
|
||||
<inkscape:date>2020-04-15</inkscape:date>
|
||||
</inkscape:templateinfo>
|
||||
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
109
Assets/Square150x150Logo.svg
Normal file
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
sodipodi:docname="disegno-1.svg"
|
||||
inkscape:version="1.0rc1 (09960d6, 2020-04-09)"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 150 150"
|
||||
height="150"
|
||||
width="150">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-height="775"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:guide-bbox="true"
|
||||
showguides="true"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:showpageshadow="false"
|
||||
showborder="true"
|
||||
showgrid="false"
|
||||
inkscape:document-rotation="0"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-units="px"
|
||||
inkscape:cy="79.075594"
|
||||
inkscape:cx="91.835957"
|
||||
inkscape:zoom="3.1892627"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base"
|
||||
units="px">
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide1919"
|
||||
orientation="0,1"
|
||||
position="0,115" />
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide1921"
|
||||
orientation="-1,0"
|
||||
position="25,150" />
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide1923"
|
||||
orientation="-1,0"
|
||||
position="125,150" />
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide1925"
|
||||
orientation="0,1"
|
||||
position="0,35" />
|
||||
<sodipodi:guide
|
||||
inkscape:color="rgb(0,0,255)"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
id="guide2879"
|
||||
orientation="-1,0"
|
||||
position="75,150" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Livello 1">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="M 105.30102,35.000001 H 75.621261 c -0.280511,0 -0.507245,0.226946 -0.507245,0.507031 v 9.424762 c 0,0.317185 0.256831,0.574219 0.574223,0.574219 h 4.069258 c 1.645878,0 2.980541,1.265976 3.123457,2.874741 v 6.461712 3.599981 34.288687 0.24297 h -0.0011 c 0.01009,0.359209 -0.004,0.712449 -0.03903,1.060352 -0.004,0.05075 -0.01078,0.100887 -0.01687,0.151252 -0.02346,0.203299 -0.0495,0.405497 -0.08894,0.603445 -0.02162,0.112852 -0.05205,0.222685 -0.07913,0.333824 -0.03647,0.147809 -0.06591,0.297986 -0.111322,0.442618 -0.268348,0.858984 -0.692608,1.655686 -1.279419,2.357038 -0.02162,0.02501 -0.04819,0.04802 -0.06953,0.07273 -0.178971,0.207343 -0.366891,0.409334 -0.574866,0.598539 -0.209904,0.190918 -0.43216,0.368381 -0.66573,0.533266 -1.660381,1.176182 -3.8952,1.693662 -6.392174,1.454752 -3.18382,-0.29928 -6.34247,-1.743142 -8.895544,-4.066054 -2.552647,-2.322703 -4.139864,-5.196589 -4.468356,-8.093086 -0.295423,-2.591254 0.481005,-4.871506 2.18469,-6.42161 0.0047,-0.0033 0.0087,-0.0064 0.012,-0.01009 0.06591,-0.05909 0.138435,-0.110939 0.206261,-0.167895 1.219262,-1.021531 2.80861,-1.640544 4.622361,-1.811191 0.02038,-0.0019 0.0401,-0.0052 0.05953,-0.0068 0.190917,-0.01687 0.385445,-0.02438 0.581689,-0.03177 0.102591,-0.0033 0.203915,-0.01078 0.308019,-0.0113 0.03454,-4.8e-4 0.06739,-0.004 0.101982,-0.004 0.06186,0 0.126053,0.008 0.187922,0.0089 0.285821,0.0047 0.573797,0.01635 0.867522,0.04137 0.03711,0.003 0.07252,0.0017 0.110695,0.0052 0.02717,0.0028 0.05397,0.0087 0.08108,0.0113 1.663794,0.166817 3.311164,0.663596 4.874493,1.416358 0.03263,0.0015 0.07038,0.0113 0.117548,0.03755 0.476528,0.263222 0.567822,-0.01915 0.581901,-0.205828 V 73.11952 67.086773 c 0,-0.406353 -0.282642,-0.765776 -0.678957,-0.853016 -8.420512,-1.853212 -16.682323,0.07574 -22.512002,5.382591 -5.092707,4.632602 -7.556192,11.317645 -6.757997,18.339929 0.712234,6.254803 3.92293,12.263883 9.041661,16.922293 4.989039,4.53896 11.341325,7.37744 17.893905,7.99477 0.902929,0.0836 1.810125,0.12666 2.69535,0.12666 6.258001,0 12.064861,-2.14583 16.350841,-6.04467 4.060086,-3.69661 6.492006,-8.77269 6.848866,-14.294994 l 0.0331,-36.217627 h 0.004 v -9.683497 h 10e-4 v -0.16894 c 0.0367,-1.709012 1.43023,-3.083992 3.148627,-3.083992 h 4.06926 c 0.31697,0 0.57422,-0.257047 0.57422,-0.574221 v -9.424867 c -4e-5,-0.28008 -0.22679,-0.507031 -0.50728,-0.507031"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.133334;stop-opacity:1"
|
||||
id="path30"
|
||||
inkscape:transform-center-x="-30.453831"
|
||||
inkscape:transform-center-y="40.048222" />
|
||||
</g>
|
||||
<inkscape:templateinfo>
|
||||
<inkscape:name>Square150x150Logo</inkscape:name>
|
||||
<inkscape:shortdesc>Medium tile</inkscape:shortdesc>
|
||||
<inkscape:date>2020-04-15</inkscape:date>
|
||||
</inkscape:templateinfo>
|
||||
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
BIN
Assets/SquareIcon1024.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
Assets/SquareIcon512.png
Normal file
After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 10 KiB |
BIN
Assets/macOs.iconset/icon_128x128@2x.png
Normal file
After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 679 B After Width: | Height: | Size: 820 B |
BIN
Assets/macOs.iconset/icon_16x16@2x.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 19 KiB |
BIN
Assets/macOs.iconset/icon_256x256@2x.png
Normal file
After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
Assets/macOs.iconset/icon_32x32@2x.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 41 KiB |
BIN
Assets/macOs.iconset/icon_512x512@2x.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
Assets/macOsIcon.psd
Normal file
BIN
Assets/macOsTemplateIcon16.psd
Normal file
BIN
Assets/macOsTemplateIcon32.psd
Normal file
144
BUILD.md
@@ -1,78 +1,92 @@
|
||||
# General information
|
||||
[](https://travis-ci.org/laurent22/joplin) [](https://ci.appveyor.com/project/laurent22/joplin)
|
||||
|
||||
- All the applications share the same library, which, for historical reasons, is in ReactNativeClient/lib. This library is copied to the relevant directories when building each app.
|
||||
- The translations are built by running CliClient/build-translation.sh. You normally don't need to run this if you haven't updated the translation since the compiled files are on the repository.
|
||||
# Building the applications
|
||||
|
||||
## macOS dependencies
|
||||
Note that all the applications share the same library, which, for historical reasons, is in `ReactNativeClient/lib`. This library is copied to the relevant directories when building each app.
|
||||
|
||||
brew install yarn node
|
||||
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
|
||||
source ~/.bash_profile
|
||||
|
||||
## Linux and Windows (WSL) dependencies
|
||||
## Required dependencies
|
||||
|
||||
- Install yarn - https://yarnpkg.com/lang/en/docs/install/
|
||||
- Install node v8.x (check with `node --version`) - https://nodejs.org/en/
|
||||
- If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
|
||||
- Install node 10+ - https://nodejs.org/en/
|
||||
- macOS, Linux: Install rsync - https://nodejs.org/en/
|
||||
- macOS: Install Cocoapods - `brew install cocoapods`
|
||||
- Windows: Install Windows Build Tools - `npm install -g windows-build-tools`
|
||||
|
||||
# Building the tools
|
||||
## Building
|
||||
|
||||
Before building any of the applications, you need to build the tools:
|
||||
Before doing anything else, from the root of the project, run:
|
||||
|
||||
```
|
||||
cd Tools
|
||||
npm install
|
||||
npm install
|
||||
|
||||
Then you can test the various applications:
|
||||
|
||||
## Testing the desktop application
|
||||
|
||||
cd ElectronClient
|
||||
npm start
|
||||
|
||||
## Testing the Terminal application
|
||||
|
||||
cd CliClient
|
||||
npm start
|
||||
|
||||
## Testing the Mobile application
|
||||
|
||||
First you need to setup React Native to build projects with native code. For this, follow the instructions on the [Get Started](https://facebook.github.io/react-native/docs/getting-started.html) tutorial, in the "React Native CLI Quickstart" tab.
|
||||
|
||||
Then:
|
||||
|
||||
cd ReactNativeClient
|
||||
npm run start-android
|
||||
# Or: npm run start-ios
|
||||
|
||||
To run the iOS application, it might be easier to open the file `ios/Joplin.xcworkspace` on XCode and run the app from there.
|
||||
|
||||
Normally the bundler should start automatically with the application. If it doesn't, run `npm start`.
|
||||
|
||||
## Building the clipper
|
||||
|
||||
cd Clipper/popup
|
||||
npm install
|
||||
npm run watch # To watch for changes
|
||||
|
||||
To test the extension please refer to the relevant pages for each browser: [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension#Trying_it_out) / [Chrome](https://developer.chrome.com/extensions/faq#faq-dev-01). Please note that the extension in dev mode will only connect to a dev instance of the desktop app (and vice-versa).
|
||||
|
||||
## Watching files
|
||||
|
||||
To make changes to the application, you'll need to rebuild any TypeScript file you've changed, and rebuild the lib. The simplest way to do all this is to watch for changes from the root of the project. Simply run this command, and it should take care of the rest:
|
||||
|
||||
npm run watch
|
||||
|
||||
Running `npm run build` would have the same effect, but without watching.
|
||||
|
||||
## Running an application with additional parameters
|
||||
|
||||
You can specify additional parameters when running the desktop or CLI application. To do so, add `--` to the `npm start` command, followed by your flags. For example:
|
||||
|
||||
npm start -- --profile ~/MyTestProfile
|
||||
|
||||
## TypeScript
|
||||
|
||||
Most of the application is written in JavaScript, however new classes and files should generally be written in [TypeScript](https://www.typescriptlang.org/). All TypeScript files are generated next to the .ts or .tsx file. So for example, if there's a file "lib/MyClass.ts", there will be a generated "lib/MyClass.js" next to it. It is implemented that way as it requires minimal changes to integrate TypeScript in the existing JavaScript code base.
|
||||
|
||||
In the current setup, `tsc` is executed from the root of the project, and will compile everything in CliClient, ElectronClient, etc. This is more convenient to have just one place to compile everything, and it also means there's only one watch command to run. However, one drawback is that TypeScript doesn't find types defined in node_modules folders in sub-directories. For example, if you install `immer` in ElectronClient, then try to use the package, TypeScript will report that it cannot find this module. In theory using `typeRoots`, it should be possible to make it find the right modules but it doesn't seem to work in this case. Currently the workaround is to install any such package at the root of the project. By doing so, TypeScript will find the type definitions and compilation will work. It's not ideal since the module is installed at the root even though it's not used, but for now that will work.
|
||||
|
||||
## Hot reload
|
||||
|
||||
If you'd like to auto-reload the desktop app on changes rather than having to quit and restart it manually each time, you can use [watchman-make](https://facebook.github.io/watchman/docs/watchman-make.html):
|
||||
|
||||
```sh
|
||||
cd ElectronClient
|
||||
watchman-make -p '**/*.js' '**/*.jsx' --run "npm start"
|
||||
```
|
||||
|
||||
# Building the Electron application
|
||||
It still requires you to quit the application each time you want it to rebuild, but at least you don't have to re-run `"npm start"` each time. Here's what the workflow loop looks like in practice:
|
||||
|
||||
```
|
||||
cd ElectronClient/app
|
||||
rsync --delete -a ../../ReactNativeClient/lib/ lib/
|
||||
npm install
|
||||
yarn dist
|
||||
```
|
||||
1. Edit and save files in your text editor.
|
||||
2. Switch to the Electron app and <kbd>cmd</kbd>+<kbd>Q</kbd> to quit it.
|
||||
3. `watchman` immediately restarts the app for you (whereas usually you'd have to switch back to the terminal, type `"npm start"`, and hit enter).
|
||||
|
||||
If there's an error `while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory`, run `sudo apt-get install libgconf-2-4`
|
||||
# Troubleshooting
|
||||
|
||||
If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`.
|
||||
|
||||
If you get the error `libtool: unrecognized option '-static'`, follow the instructions [in this post](https://stackoverflow.com/a/38552393/561309) to use the correct libtool version.
|
||||
|
||||
That will create the executable file in the `dist` directory.
|
||||
|
||||
From `/ElectronClient` you can also run `run.sh` to run the app for testing.
|
||||
|
||||
## Building Electron application on Windows
|
||||
|
||||
```
|
||||
cd Tools
|
||||
npm install
|
||||
cd ..\ElectronClient\app
|
||||
xcopy /C /I /H /R /Y /S ..\..\ReactNativeClient\lib lib
|
||||
npm install
|
||||
yarn dist
|
||||
```
|
||||
|
||||
If node-gyp does not works (MSBUILD: error MSB3428: Could not load the Visual C++ component "VCBuild.exe"), you might need to install the `windows-build-tools` using `npm install --global windows-build-tools`.
|
||||
|
||||
If `yarn dist` fails, it may need administrative rights.
|
||||
|
||||
The [building\_win32\_tips on this page](./readme/building_win32_tips.md) might be helpful.
|
||||
|
||||
# Building the Mobile application
|
||||
|
||||
First you need to setup React Native to build projects with native code. For this, follow the instructions on the [Get Started](https://facebook.github.io/react-native/docs/getting-started.html) tutorial, in the "Building Projects with Native Code" tab.
|
||||
|
||||
Then, from `/ReactNativeClient`, run `npm install`, then `react-native run-ios` or `react-native run-android`.
|
||||
|
||||
# Building the Terminal application
|
||||
|
||||
```
|
||||
cd CliClient
|
||||
npm install
|
||||
./build.sh
|
||||
rsync --delete -aP ../ReactNativeClient/locales/ build/locales/
|
||||
```
|
||||
|
||||
Run `run.sh` to start the application for testing.
|
||||
Please read for the [Build Troubleshooting Document](https://github.com/laurent22/joplin/blob/master/readme/build_troubleshooting.md) for various tips on how to get the build working.
|
@@ -1,34 +1,91 @@
|
||||
**IMPORTANT:** At the moment pull requests for new features are no longer being accepted. More info there: https://github.com/laurent22/joplin/issues/1112
|
||||
|
||||
* * *
|
||||
|
||||
# User support
|
||||
|
||||
For general discussion about Joplin, user support, software development questions, and to discuss new features, please go to the [Joplin Forum](https://discourse.joplin.cozic.net/). It is possible to login with your GitHub account.
|
||||
The [Joplin Forum](https://discourse.joplinapp.org/) is the community driven place for user support, general discussion about Joplin, problems with installation, new features and software development questions. It is possible to login with your GitHub account. Don't use the issue tracker for support questions.
|
||||
|
||||
# Reporting a bug
|
||||
|
||||
Please check first that it [has not already been reported](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue). Also consider [enabling debug mode](https://github.com/laurent22/joplin/blob/master/readme/debugging.md) before reporting the issue so that you can provide as much details as possible to help fix it.
|
||||
File bugs in the [Github Issue Tracker](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue). Please follow these guidelines:
|
||||
|
||||
If possible, **please provide a screenshot**. A screenshot showing the problem is often more useful than a paragraph describing it as it can make it immediately clear what the issue is.
|
||||
- Search existing issues first, make sure yours hasn't already been reported.
|
||||
- Please follow the template.
|
||||
- Consider [enabling debug mode](https://joplinapp.org/debugging/) so that you can provide as much details as possible when reporting the issue.
|
||||
- Stay on topic, but describe the issue in detail so that others can **reproduce** it.
|
||||
- **Provide a screenshot** if possible. A screenshot showing the problem is often more useful than a paragraph describing it.
|
||||
- For web clipper bugs, **please provide the URL causing the issue**. Sometimes the clipper works in one page but not in another so it is important to know what URL has a problem.
|
||||
|
||||
# Feature requests
|
||||
|
||||
Again, please check that it has not already been requested. If it has, simply **up-vote the issue** - the ones with the most up-votes are likely to be implemented. "+1" comments are not tracked.
|
||||
Feature requests **must be opened and discussed on the [forum](https://discourse.joplinapp.org/c/features)**. After they have been accepted, they can be added to the GitHub tracker.
|
||||
|
||||
# Creating a pull request
|
||||
Please check that your request has not already been posted on the forum or the [Github Issue Tracker](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue). If it has, **up-voting the issue or topic** increases the chances it'll be noticed and implemented in the future. "+1" comments are not tracked.
|
||||
|
||||
- If you want to add a new feature, consider asking about it before implementing it or checking existing discussions to make sure it is within the scope of the project. That scope, due to limited resources, might be narrower than you think. As a rule of thumb **if your change is likely to involve more than 50 lines of code, you should discuss it in the forum**, just so that you don't waste your time implementing something that might not be accepted.
|
||||
Avoid listing multiple requests in one topic. One topic per request makes it easier to track and discuss it.
|
||||
|
||||
- Bug fixes have a very high change of being accepted.
|
||||
Finally, when submitting a pull request, don't forget to [test your code](#automated-tests).
|
||||
|
||||
- A pull request that is relevant to the current roadmap has a very high change of being accepted.
|
||||
# Contributing to Joplin's translation
|
||||
|
||||
Joplin is available in multiple languages thanks to the help of its users. You can help translate Joplin to your language or keep it up to date. Please read the documentation about [Localisation](https://joplinapp.org/#localisation).
|
||||
|
||||
# Contributing to Joplin's code
|
||||
|
||||
If you want to start contributing to the project's code, please follow these guidelines before creating a pull request:
|
||||
|
||||
- Bug fixes are always welcome. Start by reviewing the [list of bugs](https://github.com/laurent22/joplin/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
|
||||
- A good way to easily start contributing is to pick and work on a [good first issue](https://github.com/laurent22/joplin/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). We try to make these issues as clear as possible and provide basic info on how the code should be changed, and if something is unclear feel free to ask for more information on the issue.
|
||||
- Before adding a new feature, ask about it in the [Github Issue Tracker](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue) or the [Joplin Forum](https://discourse.joplinapp.org/), or check if existing discussions exist to make sure the new functionality is desired.
|
||||
- **Changes that will consist in more than 50 lines of code should be discussed the [Joplin Forum](https://discourse.joplinapp.org/)**, so that you don't spend too much time implementing something that might not be accepted.
|
||||
- All the applications share the same backend (database, synchronisation, settings, models, business logic, etc.) so if you change something in the backend in one app, makes sure it still work in the other apps. Usually it does, but keep this in mind.
|
||||
|
||||
Building the apps is relatively easy - please [see the build instructions](https://github.com/laurent22/joplin/blob/master/BUILD.md) for more details.
|
||||
|
||||
# Coding style
|
||||
## Coding style
|
||||
|
||||
There are only two rules, but not following them means the pull request will not be accepted (it can be accepted once the issues are fixed):
|
||||
Coding style is enforced by a pre-commit hook that runs eslint. This hook is installed whenever running `npm install` on any of the application directory. If for some reason the pre-commit hook didn't get installed, you can manually install it by running `npm install` at the root of the repository.
|
||||
|
||||
- **Please use tabs, NOT spaces.**
|
||||
- **Please do not add or remove optional characters, such as spaces or colons.** Please setup your editor so that it only changes what you are working on and is not making automated changes elsewhere. The reason for this is that small white space changes make diff hard to read and can cause needless conflicts.
|
||||
For new React components, please use [React Hooks](https://reactjs.org/docs/hooks-intro.html). For new code in general, please use TypeScript (unless you are modifying a file that was originally in JavaScript).
|
||||
|
||||
For changes made to the Desktop client that affect the user interface, refer to `ElectronClient/app/theme.js` for all styling information. The goal is to create a consistent user interface to allow for easy navigation of Joplin's various features and improve the overall user experience.
|
||||
|
||||
## Automated tests
|
||||
|
||||
When submitting a pull request for a new feature or a bug fix, please add automated tests for your code whenever possible. Tests in Joplin are divided into **unit tests** and **feature tests**.
|
||||
|
||||
* **Unit tests** are used to test models, services or utility classes - they are relatively low level. Unit tests should be prefixed with the type of class that is being tested - for example "models_Folder" or "services_SearchEngine".
|
||||
|
||||
* **Feature tests** on the other hand are to test higher level functionalities such as interactions with the GUI and how they affect the underlying model. Often these tests would dispatch Redux actions, and inspect how the application state has been changed. The feature tests should be prefixed with "feature_", for example "feature_TagList". There's a good explanation on what qualifies as a feature test in [this post](https://github.com/laurent22/joplin/pull/2819#issuecomment-603502230).
|
||||
|
||||
The tests are under CliClient/tests. To get them running, you first need to build the CLI app:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
cd CliClient
|
||||
```
|
||||
|
||||
To run all the test units:
|
||||
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
To run just one particular file:
|
||||
|
||||
```sh
|
||||
npm test -- --filter=markdownUtils # Don't add the .js extension
|
||||
```
|
||||
|
||||
To filter tests. For example, to run all the test units that contain "should handle conflict" in their description:
|
||||
|
||||
```sh
|
||||
npm test -- --filter="should handle conflict"
|
||||
```
|
||||
|
||||
## About abandoned pull requests
|
||||
|
||||
It happens that a pull request is started but not finished and despite our attempts to contact the contributor, we don't hear from them again.
|
||||
|
||||
In that case we will not merge the pull request, even if only small changes are missing. Our policy is simply to close the pull request. Why? Because an unfinished pull request essentially means giving us work and moving on. We would rather not encourage this behaviour.
|
||||
|
||||
Also, please note that since we have spent time reviewing the pull request and proposing solutions, we reserve the right to re-use that knowledge to create a new pull request, potentially based on your changes.
|
||||
|
||||
We'd much prefer that you complete the pull request though, so we'll be sure to ping you a few times before that!
|
||||
|
13
CliClient/.eslintrc.js
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
'overrides': [
|
||||
{
|
||||
'files': ['tests/**/*.js'],
|
||||
'rules': {
|
||||
// Ignore all unused function arguments, because in some
|
||||
// case they are kept to indicate the function signature.
|
||||
'no-unused-vars': ['error', { 'argsIgnorePattern': '.*' }],
|
||||
'@typescript-eslint/no-unused-vars': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
6
CliClient/.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
build/
|
||||
node_modules/
|
||||
app/src
|
||||
tests-build/
|
||||
@@ -20,4 +19,7 @@ tests/sync
|
||||
out.txt
|
||||
linkToLocal.sh
|
||||
yarn-error.log
|
||||
tests/support/dropbox-auth.txt
|
||||
tests/support/dropbox-auth.txt
|
||||
tests/support/nextcloud-auth.json
|
||||
tests/support/onedrive-auth.txt
|
||||
build/
|
134
CliClient/app/LinkSelector.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
const open = require('open');
|
||||
interface LinkStoreEntry {
|
||||
link: string;
|
||||
noteX: number;
|
||||
noteY: number;
|
||||
}
|
||||
|
||||
class LinkSelector {
|
||||
noteId_: string;
|
||||
scrollTop_: number;
|
||||
renderedText_: string;
|
||||
currentLinkIndex_: number;
|
||||
linkStore_: LinkStoreEntry[];
|
||||
linkRegex_: RegExp;
|
||||
|
||||
constructor() {
|
||||
this.noteId_ = null;
|
||||
this.scrollTop_ = null; // used so 'o' won't open unhighlighted link after scrolling
|
||||
this.renderedText_ = null;
|
||||
this.currentLinkIndex_ = null;
|
||||
this.linkStore_ = null;
|
||||
this.linkRegex_ = /http:\/\/[0-9.]+:[0-9]+\/[0-9]+/g;
|
||||
}
|
||||
|
||||
get link(): string | null {
|
||||
if (this.currentLinkIndex_ === null) return null;
|
||||
return this.linkStore_[this.currentLinkIndex_].link;
|
||||
}
|
||||
|
||||
get noteX(): number | null {
|
||||
if (this.currentLinkIndex_ === null) return null;
|
||||
return this.linkStore_[this.currentLinkIndex_].noteX;
|
||||
}
|
||||
|
||||
get noteY(): number | null {
|
||||
if (this.currentLinkIndex_ === null) return null;
|
||||
return this.linkStore_[this.currentLinkIndex_].noteY;
|
||||
}
|
||||
|
||||
findLinks(renderedText: string): LinkStoreEntry[] {
|
||||
const newLinkStore: LinkStoreEntry[] = [];
|
||||
const lines: string[] = renderedText.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const matches = [...lines[i].matchAll(this.linkRegex_)];
|
||||
matches.forEach((_e, n) => {
|
||||
newLinkStore.push(
|
||||
{
|
||||
link: matches[n][0],
|
||||
noteX: matches[n].index,
|
||||
noteY: i,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
return newLinkStore;
|
||||
}
|
||||
|
||||
updateText(renderedText: string): void {
|
||||
this.currentLinkIndex_ = null;
|
||||
this.renderedText_ = renderedText;
|
||||
this.linkStore_ = this.findLinks(this.renderedText_);
|
||||
}
|
||||
|
||||
updateNote(textWidget: any): void {
|
||||
this.noteId_ = textWidget.noteId;
|
||||
this.scrollTop_ = textWidget.scrollTop_;
|
||||
this.updateText(textWidget.renderedText_);
|
||||
}
|
||||
|
||||
scrollWidget(textWidget: any): void {
|
||||
if (this.currentLinkIndex_ === null) return;
|
||||
|
||||
const noteY = this.linkStore_[this.currentLinkIndex_].noteY;
|
||||
|
||||
let viewBoxMin = textWidget.scrollTop_ + 1;
|
||||
let viewBoxMax = viewBoxMin + textWidget.innerHeight - 2;
|
||||
|
||||
if (noteY < viewBoxMin) {
|
||||
for (; noteY < viewBoxMin; textWidget.pageUp()) {
|
||||
viewBoxMin = textWidget.scrollTop_;
|
||||
viewBoxMax = viewBoxMin + textWidget.innerHeight;
|
||||
}
|
||||
return;
|
||||
|
||||
} else if (noteY > viewBoxMax) {
|
||||
for (; noteY > viewBoxMax; textWidget.pageDown()) {
|
||||
viewBoxMin = textWidget.scrollTop_;
|
||||
viewBoxMax = viewBoxMin + textWidget.innerHeight;
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
changeLink(textWidget: any, offset: number): void | null {
|
||||
if (textWidget.noteId !== this.noteId_) {
|
||||
this.updateNote(textWidget);
|
||||
this.changeLink(textWidget, offset);
|
||||
return;
|
||||
}
|
||||
if (textWidget.renderedText_ !== this.renderedText_) {
|
||||
this.updateText(textWidget.renderedText_);
|
||||
this.changeLink(textWidget, offset);
|
||||
return;
|
||||
}
|
||||
if (textWidget.scrollTop_ !== this.scrollTop_) this.scrollTop_ = textWidget.scrollTop_;
|
||||
|
||||
if (!this.linkStore_.length) return null;
|
||||
|
||||
let offsetMod = (offset + this.currentLinkIndex_) % this.linkStore_.length;
|
||||
|
||||
if (this.currentLinkIndex_ === null) {
|
||||
if (offsetMod < 0) this.currentLinkIndex_ = this.linkStore_.length + offsetMod;
|
||||
else if (!offsetMod) this.currentLinkIndex_ = 0;
|
||||
else this.currentLinkIndex_ = offsetMod - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (offsetMod < 0) offsetMod = this.linkStore_.length + offsetMod;
|
||||
|
||||
this.currentLinkIndex_ = offsetMod;
|
||||
return;
|
||||
}
|
||||
|
||||
openLink(textWidget: any): void {
|
||||
if (textWidget.noteId !== this.noteId_) return;
|
||||
if (textWidget.renderedText_ !== this.renderedText_) return;
|
||||
if (textWidget.scrollTop_ !== this.scrollTop_) return;
|
||||
open(this.linkStore_[this.currentLinkIndex_].link);
|
||||
}
|
||||
}
|
||||
|
||||
export default LinkSelector;
|
||||
|
@@ -1,14 +1,11 @@
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { netUtils } = require('lib/net-utils.js');
|
||||
|
||||
const http = require("http");
|
||||
const urlParser = require("url");
|
||||
const http = require('http');
|
||||
const urlParser = require('url');
|
||||
const enableServerDestroy = require('server-destroy');
|
||||
|
||||
class ResourceServer {
|
||||
|
||||
constructor() {
|
||||
this.server_ = null;
|
||||
this.logger_ = new Logger();
|
||||
@@ -31,7 +28,7 @@ class ResourceServer {
|
||||
|
||||
baseUrl() {
|
||||
if (!this.port_) return '';
|
||||
return 'http://127.0.0.1:' + this.port_;
|
||||
return `http://127.0.0.1:${this.port_}`;
|
||||
}
|
||||
|
||||
setLinkHandler(handler) {
|
||||
@@ -40,7 +37,7 @@ class ResourceServer {
|
||||
|
||||
async start() {
|
||||
this.port_ = await netUtils.findAvailablePort([9167, 9267, 8167, 8267]);
|
||||
if (!this.port_) {
|
||||
if (!this.port_) {
|
||||
this.logger().error('Could not find available port to start resource server. Please report the error at https://github.com/laurent22/joplin');
|
||||
return;
|
||||
}
|
||||
@@ -48,16 +45,15 @@ class ResourceServer {
|
||||
this.server_ = http.createServer();
|
||||
|
||||
this.server_.on('request', async (request, response) => {
|
||||
|
||||
const writeResponse = (message) => {
|
||||
const writeResponse = message => {
|
||||
response.write(message);
|
||||
response.end();
|
||||
}
|
||||
};
|
||||
|
||||
const url = urlParser.parse(request.url, true);
|
||||
let resourceId = url.pathname.split('/');
|
||||
if (resourceId.length < 2) {
|
||||
writeResponse('Error: could not get resource ID from path name: ' + url.pathname);
|
||||
writeResponse(`Error: could not get resource ID from path name: ${url.pathname}`);
|
||||
return;
|
||||
}
|
||||
resourceId = resourceId[1];
|
||||
@@ -66,9 +62,10 @@ class ResourceServer {
|
||||
|
||||
try {
|
||||
const done = await this.linkHandler_(resourceId, response);
|
||||
if (!done) throw new Error('Unhandled resource: ' + resourceId);
|
||||
if (!done) throw new Error(`Unhandled resource: ${resourceId}`);
|
||||
} catch (error) {
|
||||
response.setHeader('Content-Type', 'text/plain');
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
response.statusCode = 400;
|
||||
response.write(error.message);
|
||||
}
|
||||
@@ -76,7 +73,7 @@ class ResourceServer {
|
||||
response.end();
|
||||
});
|
||||
|
||||
this.server_.on('error', (error) => {
|
||||
this.server_.on('error', error => {
|
||||
this.logger().error('Resource server:', error);
|
||||
});
|
||||
|
||||
@@ -91,7 +88,6 @@ class ResourceServer {
|
||||
if (this.server_) this.server_.destroy();
|
||||
this.server_ = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ResourceServer;
|
||||
module.exports = ResourceServer;
|
||||
|
@@ -5,13 +5,13 @@ const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { reducer, defaultState } = require('lib/reducer.js');
|
||||
const { splitCommandString } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Entities = require('html-entities').AllHtmlEntities;
|
||||
const htmlentities = (new Entities()).encode;
|
||||
const htmlentities = new Entities().encode;
|
||||
|
||||
const chalk = require('chalk');
|
||||
const tk = require('terminal-kit');
|
||||
@@ -20,12 +20,10 @@ const Renderer = require('tkwidgets/framework/Renderer.js');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
|
||||
const BaseWidget = require('tkwidgets/BaseWidget.js');
|
||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||
const TextWidget = require('tkwidgets/TextWidget.js');
|
||||
const HLayoutWidget = require('tkwidgets/HLayoutWidget.js');
|
||||
const VLayoutWidget = require('tkwidgets/VLayoutWidget.js');
|
||||
const ReduxRootWidget = require('tkwidgets/ReduxRootWidget.js');
|
||||
const RootWidget = require('tkwidgets/RootWidget.js');
|
||||
const WindowWidget = require('tkwidgets/WindowWidget.js');
|
||||
|
||||
const NoteWidget = require('./gui/NoteWidget.js');
|
||||
@@ -35,9 +33,10 @@ const FolderListWidget = require('./gui/FolderListWidget.js');
|
||||
const NoteListWidget = require('./gui/NoteListWidget.js');
|
||||
const StatusBarWidget = require('./gui/StatusBarWidget.js');
|
||||
const ConsoleWidget = require('./gui/ConsoleWidget.js');
|
||||
const LinkSelector = require('./LinkSelector.js').default;
|
||||
|
||||
|
||||
class AppGui {
|
||||
|
||||
constructor(app, store, keymap) {
|
||||
try {
|
||||
this.app_ = app;
|
||||
@@ -50,12 +49,12 @@ class AppGui {
|
||||
// Some keys are directly handled by the tkwidget framework
|
||||
// so they need to be remapped in a different way.
|
||||
this.tkWidgetKeys_ = {
|
||||
'focus_next': 'TAB',
|
||||
'focus_previous': 'SHIFT_TAB',
|
||||
'move_up': 'UP',
|
||||
'move_down': 'DOWN',
|
||||
'page_down': 'PAGE_DOWN',
|
||||
'page_up': 'PAGE_UP',
|
||||
focus_next: 'TAB',
|
||||
focus_previous: 'SHIFT_TAB',
|
||||
move_up: 'UP',
|
||||
move_down: 'DOWN',
|
||||
page_down: 'PAGE_DOWN',
|
||||
page_up: 'PAGE_UP',
|
||||
};
|
||||
|
||||
this.renderer_ = null;
|
||||
@@ -64,7 +63,7 @@ class AppGui {
|
||||
|
||||
this.renderer_ = new Renderer(this.term(), this.rootWidget_);
|
||||
|
||||
this.app_.on('modelAction', async (event) => {
|
||||
this.app_.on('modelAction', async event => {
|
||||
await this.handleModelAction(event.action);
|
||||
});
|
||||
|
||||
@@ -77,13 +76,15 @@ class AppGui {
|
||||
this.currentShortcutKeys_ = [];
|
||||
this.lastShortcutKeyTime_ = 0;
|
||||
|
||||
this.linkSelector_ = new LinkSelector();
|
||||
|
||||
// Recurrent sync is setup only when the GUI is started. In
|
||||
// a regular command it's not necessary since the process
|
||||
// exits right away.
|
||||
reg.setupRecurrentSync();
|
||||
DecryptionWorker.instance().scheduleStart();
|
||||
} catch (error) {
|
||||
this.fullScreen(false);
|
||||
if (this.term_) { this.fullScreen(false); }
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -134,11 +135,11 @@ class AppGui {
|
||||
};
|
||||
folderList.name = 'folderList';
|
||||
folderList.vStretch = true;
|
||||
folderList.on('currentItemChange', async (event) => {
|
||||
folderList.on('currentItemChange', async event => {
|
||||
const item = folderList.currentItem;
|
||||
|
||||
if (item === '-') {
|
||||
let newIndex = event.currentIndex + (event.previousIndex < event.currentIndex ? +1 : -1);
|
||||
const newIndex = event.currentIndex + (event.previousIndex < event.currentIndex ? +1 : -1);
|
||||
let nextItem = folderList.itemAt(newIndex);
|
||||
if (!nextItem) nextItem = folderList.itemAt(event.previousIndex);
|
||||
|
||||
@@ -169,7 +170,7 @@ class AppGui {
|
||||
});
|
||||
}
|
||||
});
|
||||
this.rootWidget_.connect(folderList, (state) => {
|
||||
this.rootWidget_.connect(folderList, state => {
|
||||
return {
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
selectedTagId: state.selectedTagId,
|
||||
@@ -190,13 +191,13 @@ class AppGui {
|
||||
borderRightWidth: 1,
|
||||
};
|
||||
noteList.on('currentItemChange', async () => {
|
||||
let note = noteList.currentItem;
|
||||
const note = noteList.currentItem;
|
||||
this.store_.dispatch({
|
||||
type: 'NOTE_SELECT',
|
||||
id: note ? note.id : null,
|
||||
});
|
||||
});
|
||||
this.rootWidget_.connect(noteList, (state) => {
|
||||
this.rootWidget_.connect(noteList, state => {
|
||||
return {
|
||||
selectedNoteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
|
||||
items: state.notes,
|
||||
@@ -210,7 +211,7 @@ class AppGui {
|
||||
borderBottomWidth: 1,
|
||||
borderLeftWidth: 1,
|
||||
};
|
||||
this.rootWidget_.connect(noteText, (state) => {
|
||||
this.rootWidget_.connect(noteText, state => {
|
||||
return {
|
||||
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
|
||||
notes: state.notes,
|
||||
@@ -225,7 +226,7 @@ class AppGui {
|
||||
borderLeftWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
};
|
||||
this.rootWidget_.connect(noteMetadata, (state) => {
|
||||
this.rootWidget_.connect(noteMetadata, state => {
|
||||
return { noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null };
|
||||
});
|
||||
noteMetadata.hide();
|
||||
@@ -247,9 +248,9 @@ class AppGui {
|
||||
|
||||
const hLayout = new HLayoutWidget();
|
||||
hLayout.name = 'hLayout';
|
||||
hLayout.addChild(folderList, { type: 'stretch', factor: 1 });
|
||||
hLayout.addChild(noteList, { type: 'stretch', factor: 1 });
|
||||
hLayout.addChild(noteLayout, { type: 'stretch', factor: 2 });
|
||||
hLayout.addChild(folderList, { type: 'stretch', factor: Setting.value('layout.folderList.factor') });
|
||||
hLayout.addChild(noteList, { type: 'stretch', factor: Setting.value('layout.noteList.factor') });
|
||||
hLayout.addChild(noteLayout, { type: 'stretch', factor: Setting.value('layout.note.factor') });
|
||||
|
||||
const vLayout = new VLayoutWidget();
|
||||
vLayout.name = 'vLayout';
|
||||
@@ -292,7 +293,7 @@ class AppGui {
|
||||
if (!cmd) return;
|
||||
const isConfigPassword = cmd.indexOf('config ') >= 0 && cmd.indexOf('password') >= 0;
|
||||
if (isConfigPassword) return;
|
||||
this.stdout(chalk.cyan.bold('> ' + cmd));
|
||||
this.stdout(chalk.cyan.bold(`> ${cmd}`));
|
||||
}
|
||||
|
||||
setupKeymap(keymap) {
|
||||
@@ -301,7 +302,7 @@ class AppGui {
|
||||
for (let i = 0; i < keymap.length; i++) {
|
||||
const item = Object.assign({}, keymap[i]);
|
||||
|
||||
if (!item.command) throw new Error('Missing command for keymap item: ' + JSON.stringify(item));
|
||||
if (!item.command) throw new Error(`Missing command for keymap item: ${JSON.stringify(item)}`);
|
||||
|
||||
if (!('type' in item)) item.type = 'exec';
|
||||
|
||||
@@ -342,7 +343,7 @@ class AppGui {
|
||||
|
||||
if (consoleWidget.isMaximized__ === doMaximize) return;
|
||||
|
||||
let constraints = {
|
||||
const constraints = {
|
||||
type: 'stretch',
|
||||
factor: !doMaximize ? 1 : 4,
|
||||
};
|
||||
@@ -408,7 +409,7 @@ class AppGui {
|
||||
activeListItem() {
|
||||
const widget = this.widget('mainWindow').focusedWidget;
|
||||
if (!widget) return null;
|
||||
|
||||
|
||||
if (widget.name == 'noteList' || widget.name == 'folderList') {
|
||||
return widget.currentItem;
|
||||
}
|
||||
@@ -419,10 +420,10 @@ class AppGui {
|
||||
async handleModelAction(action) {
|
||||
this.logger().info('Action:', action);
|
||||
|
||||
let state = Object.assign({}, defaultState);
|
||||
const state = Object.assign({}, defaultState);
|
||||
state.notes = this.widget('noteList').items;
|
||||
|
||||
let newState = reducer(state, action);
|
||||
const newState = reducer(state, action);
|
||||
|
||||
if (newState !== state) {
|
||||
this.widget('noteList').items = newState.notes;
|
||||
@@ -430,25 +431,21 @@ class AppGui {
|
||||
}
|
||||
|
||||
async processFunctionCommand(cmd) {
|
||||
|
||||
if (cmd === 'activate') {
|
||||
|
||||
const w = this.widget('mainWindow').focusedWidget;
|
||||
if (w.name === 'folderList') {
|
||||
this.widget('noteList').focus();
|
||||
} else if (w.name === 'noteList' || w.name === 'noteText') {
|
||||
this.processPromptCommand('edit $n');
|
||||
}
|
||||
|
||||
} else if (cmd === 'delete') {
|
||||
|
||||
if (this.widget('folderList').hasFocus) {
|
||||
const item = this.widget('folderList').selectedJoplinItem;
|
||||
|
||||
if (!item) return;
|
||||
|
||||
if (item.type_ === BaseModel.TYPE_FOLDER) {
|
||||
await this.processPromptCommand('rmbook ' + item.id);
|
||||
await this.processPromptCommand(`rmbook ${item.id}`);
|
||||
} else if (item.type_ === BaseModel.TYPE_TAG) {
|
||||
this.stdout(_('To delete a tag, untag the associated notes.'));
|
||||
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
||||
@@ -462,9 +459,31 @@ class AppGui {
|
||||
} else {
|
||||
this.stdout(_('Please select the note or notebook to be deleted first.'));
|
||||
}
|
||||
} else if (cmd === 'next_link' || cmd === 'previous_link') {
|
||||
const noteText = this.widget('noteText');
|
||||
|
||||
noteText.render();
|
||||
|
||||
if (cmd === 'next_link') this.linkSelector_.changeLink(noteText, 1);
|
||||
else this.linkSelector_.changeLink(noteText, -1);
|
||||
|
||||
this.linkSelector_.scrollWidget(noteText);
|
||||
|
||||
const cursorOffsetX = this.widget('mainWindow').width - noteText.innerWidth - 8;
|
||||
const cursorOffsetY = 1 - noteText.scrollTop_;
|
||||
|
||||
if (this.linkSelector_.link) {
|
||||
this.term_.moveTo(
|
||||
this.linkSelector_.noteX + cursorOffsetX,
|
||||
this.linkSelector_.noteY + cursorOffsetY
|
||||
);
|
||||
setTimeout(() => this.term_.term().inverse(this.linkSelector_.link), 50);
|
||||
}
|
||||
} else if (cmd === 'open_link') {
|
||||
if (this.widget('noteText').hasFocus) {
|
||||
this.linkSelector_.openLink(this.widget('noteText'));
|
||||
}
|
||||
} else if (cmd === 'toggle_console') {
|
||||
|
||||
if (!this.consoleIsShown()) {
|
||||
this.showConsole();
|
||||
this.minimizeConsole();
|
||||
@@ -475,22 +494,15 @@ class AppGui {
|
||||
this.maximizeConsole();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (cmd === 'toggle_metadata') {
|
||||
|
||||
this.toggleNoteMetadata();
|
||||
|
||||
} else if (cmd === 'enter_command_line_mode') {
|
||||
|
||||
const cmd = await this.widget('statusBar').prompt();
|
||||
if (!cmd) return;
|
||||
this.addCommandToConsole(cmd);
|
||||
await this.processPromptCommand(cmd);
|
||||
|
||||
await this.processPromptCommand(cmd);
|
||||
} else {
|
||||
|
||||
throw new Error('Unknown command: ' + cmd);
|
||||
|
||||
throw new Error(`Unknown command: ${cmd}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,17 +513,17 @@ class AppGui {
|
||||
|
||||
// this.logger().debug('Got command: ' + cmd);
|
||||
|
||||
try {
|
||||
let note = this.widget('noteList').currentItem;
|
||||
let folder = this.widget('folderList').currentItem;
|
||||
let args = splitCommandString(cmd);
|
||||
try {
|
||||
const note = this.widget('noteList').currentItem;
|
||||
const folder = this.widget('folderList').currentItem;
|
||||
const args = splitCommandString(cmd);
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] == '$n') {
|
||||
args[i] = note ? note.id : '';
|
||||
} else if (args[i] == '$b') {
|
||||
args[i] = folder ? folder.id : '';
|
||||
} else if (args[i] == '$c') {
|
||||
} else if (args[i] == '$c') {
|
||||
const item = this.activeListItem();
|
||||
args[i] = item ? item.id : '';
|
||||
}
|
||||
@@ -523,7 +535,7 @@ class AppGui {
|
||||
}
|
||||
|
||||
this.widget('console').scrollBottom();
|
||||
|
||||
|
||||
// Invalidate so that the screen is redrawn in case inputting a command has moved
|
||||
// the GUI up (in particular due to autocompletion), it's moved back to the right position.
|
||||
this.widget('root').invalidate();
|
||||
@@ -565,7 +577,7 @@ class AppGui {
|
||||
stdout(text) {
|
||||
if (text === null || text === undefined) return;
|
||||
|
||||
let lines = text.split('\n');
|
||||
const lines = text.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const v = typeof lines[i] === 'object' ? JSON.stringify(lines[i]) : lines[i];
|
||||
this.widget('console').addLine(v);
|
||||
@@ -603,17 +615,17 @@ class AppGui {
|
||||
async setupResourceServer() {
|
||||
const linkStyle = chalk.blue.underline;
|
||||
const noteTextWidget = this.widget('noteText');
|
||||
const resourceIdRegex = /^:\/[a-f0-9]+$/i
|
||||
const resourceIdRegex = /^:\/[a-f0-9]+$/i;
|
||||
const noteLinks = {};
|
||||
|
||||
const hasProtocol = function(s, protocols) {
|
||||
if (!s) return false;
|
||||
s = s.trim().toLowerCase();
|
||||
for (let i = 0; i < protocols.length; i++) {
|
||||
if (s.indexOf(protocols[i] + '://') === 0) return true;
|
||||
if (s.indexOf(`${protocols[i]}://`) === 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// By default, before the server is started, only the regular
|
||||
// URLs appear in blue.
|
||||
@@ -637,29 +649,31 @@ class AppGui {
|
||||
const link = noteLinks[path];
|
||||
|
||||
if (link.type === 'url') {
|
||||
response.writeHead(302, { 'Location': link.url });
|
||||
response.writeHead(302, { Location: link.url });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (link.type === 'item') {
|
||||
const itemId = link.id;
|
||||
let item = await BaseItem.loadItemById(itemId);
|
||||
if (!item) throw new Error('No item with ID ' + itemId); // Should be nearly impossible
|
||||
const item = await BaseItem.loadItemById(itemId);
|
||||
if (!item) throw new Error(`No item with ID ${itemId}`); // Should be nearly impossible
|
||||
|
||||
if (item.type_ === BaseModel.TYPE_RESOURCE) {
|
||||
if (item.mime) response.setHeader('Content-Type', item.mime);
|
||||
response.write(await Resource.content(item));
|
||||
} else if (item.type_ === BaseModel.TYPE_NOTE) {
|
||||
const html = [`
|
||||
const html = [
|
||||
`
|
||||
<!DOCTYPE html>
|
||||
<html class="client-nojs" lang="en" dir="ltr">
|
||||
<head><meta charset="UTF-8"/></head><body>
|
||||
`];
|
||||
html.push('<pre>' + htmlentities(item.title) + '\n\n' + htmlentities(item.body) + '</pre>');
|
||||
`,
|
||||
];
|
||||
html.push(`<pre>${htmlentities(item.title)}\n\n${htmlentities(item.body)}</pre>`);
|
||||
html.push('</body></html>');
|
||||
response.write(html.join(''));
|
||||
} else {
|
||||
throw new Error('Unsupported item type: ' + item.type_);
|
||||
throw new Error(`Unsupported item type: ${item.type_}`);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -679,7 +693,7 @@ class AppGui {
|
||||
noteLinks[index] = {
|
||||
type: 'item',
|
||||
id: url.substr(2),
|
||||
};
|
||||
};
|
||||
} else if (hasProtocol(url, ['http', 'https', 'file', 'ftp'])) {
|
||||
noteLinks[index] = {
|
||||
type: 'url',
|
||||
@@ -691,7 +705,7 @@ class AppGui {
|
||||
return url;
|
||||
}
|
||||
|
||||
return linkStyle(this.resourceServer_.baseUrl() + '/' + index);
|
||||
return linkStyle(`${this.resourceServer_.baseUrl()}/${index}`);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -710,8 +724,7 @@ class AppGui {
|
||||
|
||||
term.grabInput();
|
||||
|
||||
term.on('key', async (name, matches, data) => {
|
||||
|
||||
term.on('key', async (name) => {
|
||||
// -------------------------------------------------------------------------
|
||||
// Handle special shortcuts
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -729,13 +742,13 @@ class AppGui {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === 'CTRL_C' ) {
|
||||
if (name === 'CTRL_C') {
|
||||
const cmd = this.app().currentCommand();
|
||||
if (!cmd || !cmd.cancellable() || this.commandCancelCalled_) {
|
||||
this.stdout(_('Press Ctrl+D or type "exit" to exit the application'));
|
||||
} else {
|
||||
this.commandCancelCalled_ = true;
|
||||
await cmd.cancel()
|
||||
await cmd.cancel();
|
||||
this.commandCancelCalled_ = false;
|
||||
}
|
||||
return;
|
||||
@@ -744,8 +757,8 @@ class AppGui {
|
||||
// -------------------------------------------------------------------------
|
||||
// Build up current shortcut
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
const now = (new Date()).getTime();
|
||||
|
||||
const now = new Date().getTime();
|
||||
|
||||
if (now - this.lastShortcutKeyTime_ > 800 || this.isSpecialKey(name)) {
|
||||
this.currentShortcutKeys_ = [name];
|
||||
@@ -766,7 +779,7 @@ class AppGui {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
const shortcutKey = this.currentShortcutKeys_.join('');
|
||||
let keymapItem = this.keymapItemByKey(shortcutKey);
|
||||
const keymapItem = this.keymapItemByKey(shortcutKey);
|
||||
|
||||
// If this command is an alias to another command, resolve to the actual command
|
||||
|
||||
@@ -782,7 +795,7 @@ class AppGui {
|
||||
if (keymapItem.type === 'function') {
|
||||
this.processFunctionCommand(keymapItem.command);
|
||||
} else if (keymapItem.type === 'prompt') {
|
||||
let promptOptions = {};
|
||||
const promptOptions = {};
|
||||
if ('cursorPosition' in keymapItem) promptOptions.cursorPosition = keymapItem.cursorPosition;
|
||||
const commandString = await statusBar.prompt(keymapItem.command ? keymapItem.command : '', null, promptOptions);
|
||||
this.addCommandToConsole(commandString);
|
||||
@@ -793,7 +806,7 @@ class AppGui {
|
||||
} else if (keymapItem.type === 'tkwidgets') {
|
||||
this.widget('root').handleKey(this.tkWidgetKeys_[keymapItem.command]);
|
||||
} else {
|
||||
throw new Error('Unknown command type: ' + JSON.stringify(keymapItem));
|
||||
throw new Error(`Unknown command type: ${JSON.stringify(keymapItem)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,7 +826,6 @@ class AppGui {
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AppGui.INPUT_MODE_NORMAL = 1;
|
||||
|
@@ -1,10 +1,5 @@
|
||||
const { BaseApplication } = require('lib/BaseApplication');
|
||||
const { createStore, applyMiddleware } = require('redux');
|
||||
const { reducer, defaultState } = require('lib/reducer.js');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const ResourceService = require('lib/services/ResourceService');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
@@ -12,19 +7,15 @@ const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { fileExtension } = require('lib/path-utils.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const { _, setLocale, defaultLocale, closestSupportedLocale } = require('lib/locale.js');
|
||||
const os = require('os');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const fs = require('fs-extra');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const Cache = require('lib/Cache');
|
||||
const RevisionService = require('lib/services/RevisionService');
|
||||
|
||||
class Application extends BaseApplication {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -56,7 +47,7 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
async loadItem(type, pattern, options = null) {
|
||||
let output = await this.loadItems(type, pattern, options);
|
||||
const output = await this.loadItems(type, pattern, options);
|
||||
|
||||
if (output.length > 1) {
|
||||
// output.sort((a, b) => { return a.user_updated_time < b.user_updated_time ? +1 : -1; });
|
||||
@@ -73,7 +64,7 @@ class Application extends BaseApplication {
|
||||
// const response = await cliUtils.promptMcq(msg, answers);
|
||||
// if (!response) return null;
|
||||
|
||||
return output[response - 1];
|
||||
// return output[response - 1];
|
||||
} else {
|
||||
return output.length ? output[0] : null;
|
||||
}
|
||||
@@ -95,10 +86,12 @@ class Application extends BaseApplication {
|
||||
const parent = options.parent ? options.parent : app().currentFolder();
|
||||
const ItemClass = BaseItem.itemClass(type);
|
||||
|
||||
if (type == BaseModel.TYPE_NOTE && pattern.indexOf('*') >= 0) { // Handle it as pattern
|
||||
if (type == BaseModel.TYPE_NOTE && pattern.indexOf('*') >= 0) {
|
||||
// Handle it as pattern
|
||||
if (!parent) throw new Error(_('No notebook selected.'));
|
||||
return await Note.previews(parent.id, { titlePattern: pattern });
|
||||
} else { // Single item
|
||||
} else {
|
||||
// Single item
|
||||
let item = null;
|
||||
if (type == BaseModel.TYPE_NOTE) {
|
||||
if (!parent) throw new Error(_('No notebook has been specified.'));
|
||||
@@ -124,15 +117,15 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
setupCommand(cmd) {
|
||||
cmd.setStdout((text) => {
|
||||
cmd.setStdout(text => {
|
||||
return this.stdout(text);
|
||||
});
|
||||
|
||||
cmd.setDispatcher((action) => {
|
||||
cmd.setDispatcher(action => {
|
||||
if (this.store()) {
|
||||
return this.store().dispatch(action);
|
||||
} else {
|
||||
return (action) => {};
|
||||
return () => {};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -143,15 +136,15 @@ class Application extends BaseApplication {
|
||||
if (!options.answers) options.answers = options.booleanAnswerDefault === 'y' ? [_('Y'), _('n')] : [_('N'), _('y')];
|
||||
|
||||
if (options.type == 'boolean') {
|
||||
message += ' (' + options.answers.join('/') + ')';
|
||||
message += ` (${options.answers.join('/')})`;
|
||||
}
|
||||
|
||||
let answer = await this.gui().prompt('', message + ' ', options);
|
||||
let answer = await this.gui().prompt('', `${message} `, options);
|
||||
|
||||
if (options.type === 'boolean') {
|
||||
if (answer === null) return false; // Pressed ESCAPE
|
||||
if (!answer) answer = options.answers[0];
|
||||
let positiveIndex = options.booleanAnswerDefault == 'y' ? 0 : 1;
|
||||
const positiveIndex = options.booleanAnswerDefault == 'y' ? 0 : 1;
|
||||
return answer.toLowerCase() === options.answers[positiveIndex].toLowerCase();
|
||||
} else {
|
||||
return answer;
|
||||
@@ -183,12 +176,12 @@ class Application extends BaseApplication {
|
||||
|
||||
commands(uiType = null) {
|
||||
if (!this.allCommandsLoaded_) {
|
||||
fs.readdirSync(__dirname).forEach((path) => {
|
||||
fs.readdirSync(__dirname).forEach(path => {
|
||||
if (path.indexOf('command-') !== 0) return;
|
||||
const ext = fileExtension(path)
|
||||
const ext = fileExtension(path);
|
||||
if (ext != 'js') return;
|
||||
|
||||
let CommandClass = require('./' + path);
|
||||
const CommandClass = require(`./${path}`);
|
||||
let cmd = new CommandClass();
|
||||
if (!cmd.enabled()) return;
|
||||
cmd = this.setupCommand(cmd);
|
||||
@@ -199,8 +192,8 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
if (uiType !== null) {
|
||||
let temp = [];
|
||||
for (let n in this.commands_) {
|
||||
const temp = [];
|
||||
for (const n in this.commands_) {
|
||||
if (!this.commands_.hasOwnProperty(n)) continue;
|
||||
const c = this.commands_[n];
|
||||
if (!c.supportsUi(uiType)) continue;
|
||||
@@ -214,8 +207,8 @@ class Application extends BaseApplication {
|
||||
|
||||
async commandNames() {
|
||||
const metadata = await this.commandMetadata();
|
||||
let output = [];
|
||||
for (let n in metadata) {
|
||||
const output = [];
|
||||
for (const n in metadata) {
|
||||
if (!metadata.hasOwnProperty(n)) continue;
|
||||
output.push(n);
|
||||
}
|
||||
@@ -234,7 +227,7 @@ class Application extends BaseApplication {
|
||||
const commands = this.commands();
|
||||
|
||||
output = {};
|
||||
for (let n in commands) {
|
||||
for (const n in commands) {
|
||||
if (!commands.hasOwnProperty(n)) continue;
|
||||
const cmd = commands[n];
|
||||
output[n] = cmd.metadata();
|
||||
@@ -255,10 +248,10 @@ class Application extends BaseApplication {
|
||||
|
||||
let CommandClass = null;
|
||||
try {
|
||||
CommandClass = require(__dirname + '/command-' + name + '.js');
|
||||
CommandClass = require(`${__dirname}/command-${name}.js`);
|
||||
} catch (error) {
|
||||
if (error.message && error.message.indexOf('Cannot find module') >= 0) {
|
||||
let e = new Error(_('No such command: %s', name));
|
||||
const e = new Error(_('No such command: %s', name));
|
||||
e.type = 'notFound';
|
||||
throw e;
|
||||
} else {
|
||||
@@ -274,19 +267,27 @@ class Application extends BaseApplication {
|
||||
|
||||
dummyGui() {
|
||||
return {
|
||||
isDummy: () => { return true; },
|
||||
prompt: (initialText = '', promptString = '', options = null) => { return cliUtils.prompt(initialText, promptString, options); },
|
||||
isDummy: () => {
|
||||
return true;
|
||||
},
|
||||
prompt: (initialText = '', promptString = '', options = null) => {
|
||||
return cliUtils.prompt(initialText, promptString, options);
|
||||
},
|
||||
showConsole: () => {},
|
||||
maximizeConsole: () => {},
|
||||
stdout: (text) => { console.info(text); },
|
||||
fullScreen: (b=true) => {},
|
||||
stdout: text => {
|
||||
console.info(text);
|
||||
},
|
||||
fullScreen: () => {},
|
||||
exit: () => {},
|
||||
showModalOverlay: (text) => {},
|
||||
showModalOverlay: () => {},
|
||||
hideModalOverlay: () => {},
|
||||
stdoutMaxWidth: () => { return 100; },
|
||||
stdoutMaxWidth: () => {
|
||||
return 100;
|
||||
},
|
||||
forceRender: () => {},
|
||||
termSaveState: () => {},
|
||||
termRestoreState: (state) => {},
|
||||
termRestoreState: () => {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -298,7 +299,7 @@ class Application extends BaseApplication {
|
||||
|
||||
let outException = null;
|
||||
try {
|
||||
if (this.gui().isDummy() && !this.activeCommand_.supportsUi('cli')) throw new Error(_('The command "%s" is only available in GUI mode', this.activeCommand_.name()));
|
||||
if (this.gui().isDummy() && !this.activeCommand_.supportsUi('cli')) throw new Error(_('The command "%s" is only available in GUI mode', this.activeCommand_.name()));
|
||||
const cmdArgs = cliUtils.makeCommandArgs(this.activeCommand_, argv);
|
||||
await this.activeCommand_.action(cmdArgs);
|
||||
} catch (error) {
|
||||
@@ -314,24 +315,27 @@ class Application extends BaseApplication {
|
||||
|
||||
async loadKeymaps() {
|
||||
const defaultKeyMap = [
|
||||
{ "keys": [":"], "type": "function", "command": "enter_command_line_mode" },
|
||||
{ "keys": ["TAB"], "type": "function", "command": "focus_next" },
|
||||
{ "keys": ["SHIFT_TAB"], "type": "function", "command": "focus_previous" },
|
||||
{ "keys": ["UP"], "type": "function", "command": "move_up" },
|
||||
{ "keys": ["DOWN"], "type": "function", "command": "move_down" },
|
||||
{ "keys": ["PAGE_UP"], "type": "function", "command": "page_up" },
|
||||
{ "keys": ["PAGE_DOWN"], "type": "function", "command": "page_down" },
|
||||
{ "keys": ["ENTER"], "type": "function", "command": "activate" },
|
||||
{ "keys": ["DELETE", "BACKSPACE"], "type": "function", "command": "delete" },
|
||||
{ "keys": [" "], "command": "todo toggle $n" },
|
||||
{ "keys": ["tc"], "type": "function", "command": "toggle_console" },
|
||||
{ "keys": ["tm"], "type": "function", "command": "toggle_metadata" },
|
||||
{ "keys": ["/"], "type": "prompt", "command": "search \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["mn"], "type": "prompt", "command": "mknote \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["mt"], "type": "prompt", "command": "mktodo \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["mb"], "type": "prompt", "command": "mkbook \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["yn"], "type": "prompt", "command": "cp $n \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["dn"], "type": "prompt", "command": "mv $n \"\"", "cursorPosition": -2 }
|
||||
{ keys: [':'], type: 'function', command: 'enter_command_line_mode' },
|
||||
{ keys: ['TAB'], type: 'function', command: 'focus_next' },
|
||||
{ keys: ['SHIFT_TAB'], type: 'function', command: 'focus_previous' },
|
||||
{ keys: ['UP'], type: 'function', command: 'move_up' },
|
||||
{ keys: ['DOWN'], type: 'function', command: 'move_down' },
|
||||
{ keys: ['PAGE_UP'], type: 'function', command: 'page_up' },
|
||||
{ keys: ['PAGE_DOWN'], type: 'function', command: 'page_down' },
|
||||
{ keys: ['ENTER'], type: 'function', command: 'activate' },
|
||||
{ keys: ['DELETE', 'BACKSPACE'], type: 'function', command: 'delete' },
|
||||
{ keys: ['n'], type: 'function', command: 'next_link' },
|
||||
{ keys: ['b'], type: 'function', command: 'previous_link' },
|
||||
{ keys: ['o'], type: 'function', command: 'open_link' },
|
||||
{ keys: [' '], command: 'todo toggle $n' },
|
||||
{ keys: ['tc'], type: 'function', command: 'toggle_console' },
|
||||
{ keys: ['tm'], type: 'function', command: 'toggle_metadata' },
|
||||
{ keys: ['/'], type: 'prompt', command: 'search ""', cursorPosition: -2 },
|
||||
{ keys: ['mn'], type: 'prompt', command: 'mknote ""', cursorPosition: -2 },
|
||||
{ keys: ['mt'], type: 'prompt', command: 'mktodo ""', cursorPosition: -2 },
|
||||
{ keys: ['mb'], type: 'prompt', command: 'mkbook ""', cursorPosition: -2 },
|
||||
{ keys: ['yn'], type: 'prompt', command: 'cp $n ""', cursorPosition: -2 },
|
||||
{ keys: ['dn'], type: 'prompt', command: 'mv $n ""', cursorPosition: -2 },
|
||||
];
|
||||
|
||||
// Filter the keymap item by command so that items in keymap.json can override
|
||||
@@ -339,10 +343,10 @@ class Application extends BaseApplication {
|
||||
const itemsByCommand = {};
|
||||
|
||||
for (let i = 0; i < defaultKeyMap.length; i++) {
|
||||
itemsByCommand[defaultKeyMap[i].command] = defaultKeyMap[i]
|
||||
itemsByCommand[defaultKeyMap[i].command] = defaultKeyMap[i];
|
||||
}
|
||||
|
||||
const filePath = Setting.value('profileDir') + '/keymap.json';
|
||||
const filePath = `${Setting.value('profileDir')}/keymap.json`;
|
||||
if (await fs.pathExists(filePath)) {
|
||||
try {
|
||||
let configString = await fs.readFile(filePath, 'utf-8');
|
||||
@@ -354,25 +358,37 @@ class Application extends BaseApplication {
|
||||
}
|
||||
} catch (error) {
|
||||
let msg = error.message ? error.message : '';
|
||||
msg = 'Could not load keymap ' + filePath + '\n' + msg;
|
||||
msg = `Could not load keymap ${filePath}\n${msg}`;
|
||||
error.message = msg;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const output = [];
|
||||
for (let n in itemsByCommand) {
|
||||
for (const n in itemsByCommand) {
|
||||
if (!itemsByCommand.hasOwnProperty(n)) continue;
|
||||
output.push(itemsByCommand[n]);
|
||||
}
|
||||
|
||||
// Map reserved shortcuts to their equivalent key
|
||||
// https://github.com/cronvel/terminal-kit/issues/101
|
||||
for (let i = 0; i < output.length; i++) {
|
||||
const newKeys = output[i].keys.map(k => {
|
||||
k = k.replace(/CTRL_H/g, 'BACKSPACE');
|
||||
k = k.replace(/CTRL_I/g, 'TAB');
|
||||
k = k.replace(/CTRL_M/g, 'ENTER');
|
||||
return k;
|
||||
});
|
||||
output[i].keys = newKeys;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
async start(argv) {
|
||||
argv = await super.start(argv);
|
||||
|
||||
cliUtils.setStdout((object) => {
|
||||
cliUtils.setStdout(object => {
|
||||
return this.stdout(object);
|
||||
});
|
||||
|
||||
@@ -383,6 +399,8 @@ class Application extends BaseApplication {
|
||||
|
||||
this.currentFolder_ = await Folder.load(Setting.value('activeFolderId'));
|
||||
|
||||
await this.applySettingsSideEffects();
|
||||
|
||||
try {
|
||||
await this.execCommand(argv);
|
||||
} catch (error) {
|
||||
@@ -393,7 +411,14 @@ class Application extends BaseApplication {
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
} else { // Otherwise open the GUI
|
||||
|
||||
await Setting.saveAll();
|
||||
|
||||
// Need to call exit() explicitly, otherwise Node wait for any timeout to complete
|
||||
// https://stackoverflow.com/questions/18050095
|
||||
process.exit(0);
|
||||
} else {
|
||||
// Otherwise open the GUI
|
||||
this.initRedux();
|
||||
|
||||
const keymap = await this.loadKeymaps();
|
||||
@@ -414,6 +439,8 @@ class Application extends BaseApplication {
|
||||
|
||||
ResourceService.runInBackground();
|
||||
|
||||
RevisionService.instance().runInBackground();
|
||||
|
||||
this.dispatch({
|
||||
type: 'TAG_UPDATE_ALL',
|
||||
items: tags,
|
||||
@@ -425,7 +452,6 @@ class Application extends BaseApplication {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let application_ = null;
|
||||
@@ -436,4 +462,4 @@ function app() {
|
||||
return application_;
|
||||
}
|
||||
|
||||
module.exports = { app };
|
||||
module.exports = { app };
|
||||
|
@@ -1,51 +1,56 @@
|
||||
var { app } = require('./app.js');
|
||||
var Note = require('lib/models/Note.js');
|
||||
var Folder = require('lib/models/Folder.js');
|
||||
var Tag = require('lib/models/Tag.js');
|
||||
var { cliUtils } = require('./cli-utils.js');
|
||||
var yargParser = require('yargs-parser');
|
||||
var fs = require('fs-extra');
|
||||
const { app } = require('./app.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const yargParser = require('yargs-parser');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
async function handleAutocompletionPromise(line) {
|
||||
// Auto-complete the command name
|
||||
const names = await app().commandNames();
|
||||
let words = getArguments(line);
|
||||
//If there is only one word and it is not already a command name then you
|
||||
//should look for commmands it could be
|
||||
const words = getArguments(line);
|
||||
// If there is only one word and it is not already a command name then you
|
||||
// should look for commands it could be
|
||||
if (words.length == 1) {
|
||||
if (names.indexOf(words[0]) === -1) {
|
||||
let x = names.filter((n) => n.indexOf(words[0]) === 0);
|
||||
const x = names.filter(n => n.indexOf(words[0]) === 0);
|
||||
if (x.length === 1) {
|
||||
return x[0] + ' ';
|
||||
return `${x[0]} `;
|
||||
}
|
||||
return x.length > 0 ? x.map((a) => a + ' ') : line;
|
||||
return x.length > 0 ? x.map(a => `${a} `) : line;
|
||||
} else {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
//There is more than one word and it is a command
|
||||
// There is more than one word and it is a command
|
||||
const metadata = (await app().commandMetadata())[words[0]];
|
||||
//If for some reason this command does not have any associated metadata
|
||||
//just don't autocomplete. However, this should not happen.
|
||||
// If for some reason this command does not have any associated metadata
|
||||
// just don't autocomplete. However, this should not happen.
|
||||
if (metadata === undefined) {
|
||||
return line;
|
||||
}
|
||||
//complete an option
|
||||
let next = words.length > 1 ? words[words.length - 1] : '';
|
||||
let l = [];
|
||||
|
||||
if (words[0] === 'tag' && words[1] === 'notetags') {
|
||||
metadata.usage = 'tag <tag-command> <note>';
|
||||
}
|
||||
|
||||
// complete an option
|
||||
const next = words.length > 1 ? words[words.length - 1] : '';
|
||||
const l = [];
|
||||
if (next[0] === '-') {
|
||||
for (let i = 0; i<metadata.options.length; i++) {
|
||||
for (let i = 0; i < metadata.options.length; i++) {
|
||||
const options = metadata.options[i][0].split(' ');
|
||||
//if there are multiple options then they will be separated by comma and
|
||||
//space. The comma should be removed
|
||||
// if there are multiple options then they will be separated by comma and
|
||||
// space. The comma should be removed
|
||||
if (options[0][options[0].length - 1] === ',') {
|
||||
options[0] = options[0].slice(0, -1);
|
||||
}
|
||||
if (words.includes(options[0]) || words.includes(options[1])) {
|
||||
continue;
|
||||
}
|
||||
//First two elements are the flag and the third is the description
|
||||
//Only autocomplete long
|
||||
// First two elements are the flag and the third is the description
|
||||
// Only autocomplete long
|
||||
if (options.length > 1 && options[1].indexOf(next) === 0) {
|
||||
l.push(options[1]);
|
||||
} else if (options[0].indexOf(next) === 0) {
|
||||
@@ -55,70 +60,68 @@ async function handleAutocompletionPromise(line) {
|
||||
if (l.length === 0) {
|
||||
return line;
|
||||
}
|
||||
let ret = l.map(a=>toCommandLine(a));
|
||||
ret.prefix = toCommandLine(words.slice(0, -1)) + ' ';
|
||||
const ret = l.map(a => toCommandLine(a));
|
||||
ret.prefix = `${toCommandLine(words.slice(0, -1))} `;
|
||||
return ret;
|
||||
}
|
||||
//Complete an argument
|
||||
//Determine the number of positional arguments by counting the number of
|
||||
//words that don't start with a - less one for the command name
|
||||
const positionalArgs = words.filter((a)=>a.indexOf('-') !== 0).length - 1;
|
||||
// Complete an argument
|
||||
// Determine the number of positional arguments by counting the number of
|
||||
// words that don't start with a - less one for the command name
|
||||
const positionalArgs = words.filter(a => a.indexOf('-') !== 0).length - 1;
|
||||
|
||||
let cmdUsage = yargParser(metadata.usage)['_'];
|
||||
const cmdUsage = yargParser(metadata.usage)['_'];
|
||||
cmdUsage.splice(0, 1);
|
||||
|
||||
if (cmdUsage.length >= positionalArgs) {
|
||||
|
||||
let argName = cmdUsage[positionalArgs - 1];
|
||||
argName = cliUtils.parseCommandArg(argName).name;
|
||||
|
||||
const currentFolder = app().currentFolder();
|
||||
|
||||
if (argName == 'note' || argName == 'note-pattern') {
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: next + '*' }) : [];
|
||||
l.push(...notes.map((n) => n.title));
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: `${next}*` }) : [];
|
||||
l.push(...notes.map(n => n.title));
|
||||
}
|
||||
|
||||
if (argName == 'notebook') {
|
||||
const folders = await Folder.search({ titlePattern: next + '*' });
|
||||
l.push(...folders.map((n) => n.title));
|
||||
const folders = await Folder.search({ titlePattern: `${next}*` });
|
||||
l.push(...folders.map(n => n.title));
|
||||
}
|
||||
|
||||
if (argName == 'item') {
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: next + '*' }) : [];
|
||||
const folders = await Folder.search({ titlePattern: next + '*' });
|
||||
l.push(...notes.map((n) => n.title), folders.map((n) => n.title));
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: `${next}*` }) : [];
|
||||
const folders = await Folder.search({ titlePattern: `${next}*` });
|
||||
l.push(...notes.map(n => n.title), folders.map(n => n.title));
|
||||
}
|
||||
|
||||
if (argName == 'tag') {
|
||||
let tags = await Tag.search({ titlePattern: next + '*' });
|
||||
l.push(...tags.map((n) => n.title));
|
||||
const tags = await Tag.search({ titlePattern: `${next}*` });
|
||||
l.push(...tags.map(n => n.title));
|
||||
}
|
||||
|
||||
if (argName == 'file') {
|
||||
let files = await fs.readdir('.');
|
||||
const files = await fs.readdir('.');
|
||||
l.push(...files);
|
||||
}
|
||||
|
||||
if (argName == 'tag-command') {
|
||||
let c = filterList(['add', 'remove', 'list'], next);
|
||||
const c = filterList(['add', 'remove', 'list', 'notetags'], next);
|
||||
l.push(...c);
|
||||
}
|
||||
|
||||
if (argName == 'todo-command') {
|
||||
let c = filterList(['toggle', 'clear'], next);
|
||||
const c = filterList(['toggle', 'clear'], next);
|
||||
l.push(...c);
|
||||
}
|
||||
}
|
||||
if (l.length === 1) {
|
||||
return toCommandLine([...words.slice(0, -1), l[0]]);
|
||||
} else if (l.length > 1) {
|
||||
let ret = l.map(a=>toCommandLine(a));
|
||||
ret.prefix = toCommandLine(words.slice(0, -1)) + ' ';
|
||||
const ret = l.map(a => toCommandLine(a));
|
||||
ret.prefix = `${toCommandLine(words.slice(0, -1))} `;
|
||||
return ret;
|
||||
}
|
||||
return line;
|
||||
|
||||
}
|
||||
function handleAutocompletion(str, callback) {
|
||||
handleAutocompletionPromise(str).then(function(res) {
|
||||
@@ -127,22 +130,24 @@ function handleAutocompletion(str, callback) {
|
||||
}
|
||||
function toCommandLine(args) {
|
||||
if (Array.isArray(args)) {
|
||||
return args.map(function(a) {
|
||||
if(a.indexOf('"') !== -1 || a.indexOf(' ') !== -1) {
|
||||
return "'" + a + "'";
|
||||
} else if (a.indexOf("'") !== -1) {
|
||||
return '"' + a + '"';
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}).join(' ');
|
||||
return args
|
||||
.map(function(a) {
|
||||
if (a.indexOf('"') !== -1 || a.indexOf(' ') !== -1) {
|
||||
return `'${a}'`;
|
||||
} else if (a.indexOf('\'') !== -1) {
|
||||
return `"${a}"`;
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
})
|
||||
.join(' ');
|
||||
} else {
|
||||
if(args.indexOf('"') !== -1 || args.indexOf(' ') !== -1) {
|
||||
return "'" + args + "' ";
|
||||
} else if (args.indexOf("'") !== -1) {
|
||||
return '"' + args + '" ';
|
||||
if (args.indexOf('"') !== -1 || args.indexOf(' ') !== -1) {
|
||||
return `'${args}' `;
|
||||
} else if (args.indexOf('\'') !== -1) {
|
||||
return `"${args}" `;
|
||||
} else {
|
||||
return args + ' ';
|
||||
return `${args} `;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,28 +155,27 @@ function getArguments(line) {
|
||||
let inSingleQuotes = false;
|
||||
let inDoubleQuotes = false;
|
||||
let currentWord = '';
|
||||
let parsed = [];
|
||||
for(let i = 0; i<line.length; i++) {
|
||||
if(line[i] === '"') {
|
||||
if(inDoubleQuotes) {
|
||||
const parsed = [];
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
if (line[i] === '"') {
|
||||
if (inDoubleQuotes) {
|
||||
inDoubleQuotes = false;
|
||||
//maybe push word to parsed?
|
||||
//currentWord += '"';
|
||||
// maybe push word to parsed?
|
||||
// currentWord += '"';
|
||||
} else {
|
||||
inDoubleQuotes = true;
|
||||
//currentWord += '"';
|
||||
// currentWord += '"';
|
||||
}
|
||||
} else if(line[i] === "'") {
|
||||
if(inSingleQuotes) {
|
||||
} else if (line[i] === '\'') {
|
||||
if (inSingleQuotes) {
|
||||
inSingleQuotes = false;
|
||||
//maybe push word to parsed?
|
||||
//currentWord += "'";
|
||||
// maybe push word to parsed?
|
||||
// currentWord += "'";
|
||||
} else {
|
||||
inSingleQuotes = true;
|
||||
//currentWord += "'";
|
||||
// currentWord += "'";
|
||||
}
|
||||
} else if (/\s/.test(line[i]) &&
|
||||
!(inDoubleQuotes || inSingleQuotes)) {
|
||||
} else if (/\s/.test(line[i]) && !(inDoubleQuotes || inSingleQuotes)) {
|
||||
if (currentWord !== '') {
|
||||
parsed.push(currentWord);
|
||||
currentWord = '';
|
||||
@@ -188,7 +192,7 @@ function getArguments(line) {
|
||||
return parsed;
|
||||
}
|
||||
function filterList(list, next) {
|
||||
let output = [];
|
||||
const output = [];
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].indexOf(next) !== 0) continue;
|
||||
output.push(list[i]);
|
||||
|
@@ -2,7 +2,6 @@ const { _ } = require('lib/locale.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
|
||||
class BaseCommand {
|
||||
|
||||
constructor() {
|
||||
this.stdout_ = null;
|
||||
this.prompt_ = null;
|
||||
@@ -20,7 +19,7 @@ class BaseCommand {
|
||||
throw new Error('Description not defined');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
async action() {
|
||||
throw new Error('Action not defined');
|
||||
}
|
||||
|
||||
@@ -51,7 +50,7 @@ class BaseCommand {
|
||||
async cancel() {}
|
||||
|
||||
name() {
|
||||
let r = this.usage().split(' ');
|
||||
const r = this.usage().split(' ');
|
||||
return r[0];
|
||||
}
|
||||
|
||||
@@ -93,7 +92,6 @@ class BaseCommand {
|
||||
logger() {
|
||||
return reg.logger();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = { BaseCommand };
|
||||
module.exports = { BaseCommand };
|
||||
|
@@ -1,7 +1,7 @@
|
||||
const fs = require('fs-extra');
|
||||
const { fileExtension, basename, dirname } = require('lib/path-utils.js');
|
||||
const { fileExtension, dirname } = require('lib/path-utils.js');
|
||||
const wrap_ = require('word-wrap');
|
||||
const { _, setLocale, languageCode } = require('lib/locale.js');
|
||||
const { languageCode } = require('lib/locale.js');
|
||||
|
||||
const rootDir = dirname(dirname(__dirname));
|
||||
const MAX_WIDTH = 78;
|
||||
@@ -15,25 +15,25 @@ function wrap(text, indent) {
|
||||
}
|
||||
|
||||
function renderOptions(options) {
|
||||
let output = [];
|
||||
const output = [];
|
||||
const optionColWidth = getOptionColWidth(options);
|
||||
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
let option = options[i];
|
||||
const option = options[i];
|
||||
const flag = option[0];
|
||||
const indent = INDENT + INDENT + ' '.repeat(optionColWidth + 2);
|
||||
|
||||
|
||||
let r = wrap(option[1], indent);
|
||||
r = r.substr(flag.length + (INDENT + INDENT).length);
|
||||
r = INDENT + INDENT + flag + r;
|
||||
output.push(r);
|
||||
}
|
||||
|
||||
return output.join("\n");
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
function renderCommand(cmd) {
|
||||
let output = [];
|
||||
const output = [];
|
||||
output.push(INDENT + cmd.usage());
|
||||
output.push('');
|
||||
output.push(wrap(cmd.description(), INDENT + INDENT));
|
||||
@@ -44,18 +44,18 @@ function renderCommand(cmd) {
|
||||
output.push('');
|
||||
output.push(optionString);
|
||||
}
|
||||
return output.join("\n");
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
function getCommands() {
|
||||
let output = [];
|
||||
fs.readdirSync(__dirname).forEach((path) => {
|
||||
const output = [];
|
||||
fs.readdirSync(__dirname).forEach(path => {
|
||||
if (path.indexOf('command-') !== 0) return;
|
||||
const ext = fileExtension(path)
|
||||
const ext = fileExtension(path);
|
||||
if (ext != 'js') return;
|
||||
|
||||
let CommandClass = require('./' + path);
|
||||
let cmd = new CommandClass();
|
||||
const CommandClass = require(`./${path}`);
|
||||
const cmd = new CommandClass();
|
||||
if (!cmd.enabled()) return;
|
||||
if (cmd.hidden()) return;
|
||||
output.push(cmd);
|
||||
@@ -73,7 +73,7 @@ function getOptionColWidth(options) {
|
||||
}
|
||||
|
||||
function getHeader() {
|
||||
let output = [];
|
||||
const output = [];
|
||||
|
||||
output.push('NAME');
|
||||
output.push('');
|
||||
@@ -84,56 +84,56 @@ function getHeader() {
|
||||
output.push('DESCRIPTION');
|
||||
output.push('');
|
||||
|
||||
let description = [];
|
||||
const description = [];
|
||||
description.push('Joplin is a note taking and to-do application, which can handle a large number of notes organised into notebooks.');
|
||||
description.push('The notes are searchable, can be copied, tagged and modified with your own text editor.');
|
||||
description.push("\n\n");
|
||||
description.push('\n\n');
|
||||
description.push('The notes can be synchronised with various target including the file system (for example with a network directory) or with Microsoft OneDrive.');
|
||||
description.push("\n\n");
|
||||
description.push('\n\n');
|
||||
description.push('Notes exported from Evenotes via .enex files can be imported into Joplin, including the formatted content, resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.).');
|
||||
|
||||
output.push(wrap(description.join(''), INDENT));
|
||||
|
||||
return output.join("\n");
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
function getFooter() {
|
||||
let output = [];
|
||||
const output = [];
|
||||
|
||||
output.push('WEBSITE');
|
||||
output.push('');
|
||||
output.push(INDENT + 'https://joplin.cozic.net');
|
||||
output.push(`${INDENT}https://joplinapp.org`);
|
||||
|
||||
output.push('');
|
||||
|
||||
output.push('LICENSE');
|
||||
output.push('');
|
||||
let filePath = rootDir + '/LICENSE_' + languageCode();
|
||||
if (!fs.existsSync(filePath)) filePath = rootDir + '/LICENSE';
|
||||
let filePath = `${rootDir}/LICENSE_${languageCode()}`;
|
||||
if (!fs.existsSync(filePath)) filePath = `${rootDir}/LICENSE`;
|
||||
const licenseText = fs.readFileSync(filePath, 'utf8');
|
||||
output.push(wrap(licenseText, INDENT));
|
||||
|
||||
return output.join("\n");
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// setLocale('fr_FR');
|
||||
|
||||
const commands = getCommands();
|
||||
let commandBlocks = [];
|
||||
const commandBlocks = [];
|
||||
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
let cmd = commands[i];
|
||||
const cmd = commands[i];
|
||||
commandBlocks.push(renderCommand(cmd));
|
||||
}
|
||||
|
||||
const headerText = getHeader();
|
||||
const commandsText = commandBlocks.join("\n\n");
|
||||
const commandsText = commandBlocks.join('\n\n');
|
||||
const footerText = getFooter();
|
||||
|
||||
console.info(headerText + "\n\n" + 'USAGE' + "\n\n" + commandsText + "\n\n" + footerText);
|
||||
console.info(`${headerText}\n\n` + 'USAGE' + `\n\n${commandsText}\n\n${footerText}`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
main().catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
"use strict"
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
@@ -10,14 +10,14 @@ const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const exec = require('child_process').exec
|
||||
const exec = require('child_process').exec;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.error('Unhandled promise rejection', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
const baseDir = dirname(__dirname) + '/tests/cli-integration';
|
||||
const joplinAppPath = __dirname + '/main.js';
|
||||
const baseDir = `${dirname(__dirname)}/tests/cli-integration`;
|
||||
const joplinAppPath = `${__dirname}/main.js`;
|
||||
|
||||
const logger = new Logger();
|
||||
logger.addTarget('console');
|
||||
@@ -32,17 +32,17 @@ db.setLogger(dbLogger);
|
||||
|
||||
function createClient(id) {
|
||||
return {
|
||||
'id': id,
|
||||
'profileDir': baseDir + '/client' + id,
|
||||
id: id,
|
||||
profileDir: `${baseDir}/client${id}`,
|
||||
};
|
||||
}
|
||||
|
||||
const client = createClient(1);
|
||||
|
||||
function execCommand(client, command, options = {}) {
|
||||
let exePath = 'node ' + joplinAppPath;
|
||||
let cmd = exePath + ' --update-geolocation-disabled --env dev --profile ' + client.profileDir + ' ' + command;
|
||||
logger.info(client.id + ': ' + command);
|
||||
function execCommand(client, command) {
|
||||
const exePath = `node ${joplinAppPath}`;
|
||||
const cmd = `${exePath} --update-geolocation-disabled --env dev --profile ${client.profileDir} ${command}`;
|
||||
logger.info(`${client.id}: ${command}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
@@ -72,14 +72,7 @@ function assertEquals(expected, real) {
|
||||
}
|
||||
|
||||
async function clearDatabase() {
|
||||
await db.transactionExecBatch([
|
||||
'DELETE FROM folders',
|
||||
'DELETE FROM notes',
|
||||
'DELETE FROM tags',
|
||||
'DELETE FROM note_tags',
|
||||
'DELETE FROM resources',
|
||||
'DELETE FROM deleted_items',
|
||||
]);
|
||||
await db.transactionExecBatch(['DELETE FROM folders', 'DELETE FROM notes', 'DELETE FROM tags', 'DELETE FROM note_tags', 'DELETE FROM resources', 'DELETE FROM deleted_items']);
|
||||
}
|
||||
|
||||
const testUnits = {};
|
||||
@@ -101,7 +94,7 @@ testUnits.testFolders = async () => {
|
||||
|
||||
folders = await Folder.all();
|
||||
assertEquals(0, folders.length);
|
||||
}
|
||||
};
|
||||
|
||||
testUnits.testNotes = async () => {
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
@@ -121,23 +114,23 @@ testUnits.testNotes = async () => {
|
||||
notes = await Note.all();
|
||||
assertEquals(2, notes.length);
|
||||
|
||||
await execCommand(client, "rm -f 'blabla*'");
|
||||
await execCommand(client, 'rm -f \'blabla*\'');
|
||||
|
||||
notes = await Note.all();
|
||||
assertEquals(2, notes.length);
|
||||
|
||||
await execCommand(client, "rm -f 'n*'");
|
||||
await execCommand(client, 'rm -f \'n*\'');
|
||||
|
||||
notes = await Note.all();
|
||||
assertEquals(0, notes.length);
|
||||
}
|
||||
};
|
||||
|
||||
testUnits.testCat = async () => {
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
await execCommand(client, 'mknote mynote');
|
||||
|
||||
let folder = await Folder.loadByTitle('nb1');
|
||||
let note = await Note.loadFolderNoteByField(folder.id, 'title', 'mynote');
|
||||
const folder = await Folder.loadByTitle('nb1');
|
||||
const note = await Note.loadFolderNoteByField(folder.id, 'title', 'mynote');
|
||||
|
||||
let r = await execCommand(client, 'cat mynote');
|
||||
assertTrue(r.indexOf('mynote') >= 0);
|
||||
@@ -145,7 +138,7 @@ testUnits.testCat = async () => {
|
||||
|
||||
r = await execCommand(client, 'cat -v mynote');
|
||||
assertTrue(r.indexOf(note.id) >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
testUnits.testConfig = async () => {
|
||||
await execCommand(client, 'config editor vim');
|
||||
@@ -156,10 +149,10 @@ testUnits.testConfig = async () => {
|
||||
await Setting.load();
|
||||
assertEquals('subl', Setting.value('editor'));
|
||||
|
||||
let r = await execCommand(client, 'config');
|
||||
const r = await execCommand(client, 'config');
|
||||
assertTrue(r.indexOf('editor') >= 0);
|
||||
assertTrue(r.indexOf('subl') >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
testUnits.testCp = async () => {
|
||||
await execCommand(client, 'mkbook nb2');
|
||||
@@ -168,29 +161,29 @@ testUnits.testCp = async () => {
|
||||
|
||||
await execCommand(client, 'cp n1');
|
||||
|
||||
let f1 = await Folder.loadByTitle('nb1');
|
||||
let f2 = await Folder.loadByTitle('nb2');
|
||||
const f1 = await Folder.loadByTitle('nb1');
|
||||
const f2 = await Folder.loadByTitle('nb2');
|
||||
let notes = await Note.previews(f1.id);
|
||||
|
||||
assertEquals(2, notes.length);
|
||||
|
||||
await execCommand(client, 'cp n1 nb2');
|
||||
let notesF1 = await Note.previews(f1.id);
|
||||
const notesF1 = await Note.previews(f1.id);
|
||||
assertEquals(2, notesF1.length);
|
||||
notes = await Note.previews(f2.id);
|
||||
assertEquals(1, notes.length);
|
||||
assertEquals(notesF1[0].title, notes[0].title);
|
||||
}
|
||||
};
|
||||
|
||||
testUnits.testLs = async () => {
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
await execCommand(client, 'mknote note1');
|
||||
await execCommand(client, 'mknote note2');
|
||||
let r = await execCommand(client, 'ls');
|
||||
const r = await execCommand(client, 'ls');
|
||||
|
||||
assertTrue(r.indexOf('note1') >= 0);
|
||||
assertTrue(r.indexOf('note2') >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
testUnits.testMv = async () => {
|
||||
await execCommand(client, 'mkbook nb2');
|
||||
@@ -198,8 +191,8 @@ testUnits.testMv = async () => {
|
||||
await execCommand(client, 'mknote n1');
|
||||
await execCommand(client, 'mv n1 nb2');
|
||||
|
||||
let f1 = await Folder.loadByTitle('nb1');
|
||||
let f2 = await Folder.loadByTitle('nb2');
|
||||
const f1 = await Folder.loadByTitle('nb1');
|
||||
const f2 = await Folder.loadByTitle('nb2');
|
||||
let notes1 = await Note.previews(f1.id);
|
||||
let notes2 = await Note.previews(f2.id);
|
||||
|
||||
@@ -210,40 +203,40 @@ testUnits.testMv = async () => {
|
||||
await execCommand(client, 'mknote note2');
|
||||
await execCommand(client, 'mknote note3');
|
||||
await execCommand(client, 'mknote blabla');
|
||||
await execCommand(client, "mv 'note*' nb2");
|
||||
await execCommand(client, 'mv \'note*\' nb2');
|
||||
|
||||
notes1 = await Note.previews(f1.id);
|
||||
notes2 = await Note.previews(f2.id);
|
||||
|
||||
assertEquals(1, notes1.length);
|
||||
assertEquals(4, notes2.length);
|
||||
}
|
||||
};
|
||||
|
||||
async function main(argv) {
|
||||
async function main() {
|
||||
await fs.remove(baseDir);
|
||||
|
||||
logger.info(await execCommand(client, 'version'));
|
||||
|
||||
await db.open({ name: client.profileDir + '/database.sqlite' });
|
||||
BaseModel.db_ = db;
|
||||
await db.open({ name: `${client.profileDir}/database.sqlite` });
|
||||
BaseModel.setDb(db);
|
||||
await Setting.load();
|
||||
|
||||
let onlyThisTest = 'testMv';
|
||||
onlyThisTest = '';
|
||||
|
||||
for (let n in testUnits) {
|
||||
for (const n in testUnits) {
|
||||
if (!testUnits.hasOwnProperty(n)) continue;
|
||||
if (onlyThisTest && n != onlyThisTest) continue;
|
||||
|
||||
await clearDatabase();
|
||||
let testName = n.substr(4).toLowerCase();
|
||||
process.stdout.write(testName + ': ');
|
||||
const testName = n.substr(4).toLowerCase();
|
||||
process.stdout.write(`${testName}: `);
|
||||
await testUnits[n]();
|
||||
console.info('');
|
||||
}
|
||||
}
|
||||
|
||||
main(process.argv).catch((error) => {
|
||||
main(process.argv).catch(error => {
|
||||
console.info('');
|
||||
logger.error(error);
|
||||
});
|
||||
});
|
||||
|
@@ -2,45 +2,45 @@ const yargParser = require('yargs-parser');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const stringPadding = require('string-padding');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
|
||||
const cliUtils = {};
|
||||
|
||||
cliUtils.printArray = function(logFunction, rows, headers = null) {
|
||||
cliUtils.printArray = function(logFunction, rows) {
|
||||
if (!rows.length) return '';
|
||||
|
||||
const ALIGN_LEFT = 0;
|
||||
const ALIGN_RIGHT = 1;
|
||||
|
||||
let colWidths = [];
|
||||
let colAligns = [];
|
||||
const colWidths = [];
|
||||
const colAligns = [];
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
|
||||
const row = rows[i];
|
||||
|
||||
for (let j = 0; j < row.length; j++) {
|
||||
let item = row[j];
|
||||
let width = item ? item.toString().length : 0;
|
||||
let align = typeof item == 'number' ? ALIGN_RIGHT : ALIGN_LEFT;
|
||||
const item = row[j];
|
||||
const width = item ? item.toString().length : 0;
|
||||
const align = typeof item == 'number' ? ALIGN_RIGHT : ALIGN_LEFT;
|
||||
if (!colWidths[j] || colWidths[j] < width) colWidths[j] = width;
|
||||
if (colAligns.length <= j) colAligns[j] = align;
|
||||
}
|
||||
}
|
||||
|
||||
let lines = [];
|
||||
for (let row = 0; row < rows.length; row++) {
|
||||
let line = [];
|
||||
const line = [];
|
||||
for (let col = 0; col < colWidths.length; col++) {
|
||||
let item = rows[row][col];
|
||||
let width = colWidths[col];
|
||||
let dir = colAligns[col] == ALIGN_LEFT ? stringPadding.RIGHT : stringPadding.LEFT;
|
||||
const item = rows[row][col];
|
||||
const width = colWidths[col];
|
||||
const dir = colAligns[col] == ALIGN_LEFT ? stringPadding.RIGHT : stringPadding.LEFT;
|
||||
line.push(stringPadding(item, width, ' ', dir));
|
||||
}
|
||||
logFunction(line.join(' '));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cliUtils.parseFlags = function(flags) {
|
||||
let output = {};
|
||||
const output = {};
|
||||
flags = flags.split(',');
|
||||
for (let i = 0; i < flags.length; i++) {
|
||||
let f = flags[i].trim();
|
||||
@@ -56,10 +56,10 @@ cliUtils.parseFlags = function(flags) {
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
cliUtils.parseCommandArg = function(arg) {
|
||||
if (arg.length <= 2) throw new Error('Invalid command arg: ' + arg);
|
||||
if (arg.length <= 2) throw new Error(`Invalid command arg: ${arg}`);
|
||||
|
||||
const c1 = arg[0];
|
||||
const c2 = arg[arg.length - 1];
|
||||
@@ -70,22 +70,21 @@ cliUtils.parseCommandArg = function(arg) {
|
||||
} else if (c1 == '[' && c2 == ']') {
|
||||
return { required: false, name: name };
|
||||
} else {
|
||||
throw new Error('Invalid command arg: ' + arg);
|
||||
throw new Error(`Invalid command arg: ${arg}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
let cmdUsage = cmd.usage();
|
||||
cmdUsage = yargParser(cmdUsage);
|
||||
let output = {};
|
||||
const output = {};
|
||||
|
||||
let options = cmd.options();
|
||||
let booleanFlags = [];
|
||||
let aliases = {};
|
||||
const options = cmd.options();
|
||||
const booleanFlags = [];
|
||||
const aliases = {};
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
if (options[i].length != 2) throw new Error('Invalid options: ' + options[i]);
|
||||
if (options[i].length != 2) throw new Error(`Invalid options: ${options[i]}`);
|
||||
let flags = options[i][0];
|
||||
let text = options[i][1];
|
||||
|
||||
flags = cliUtils.parseFlags(flags);
|
||||
|
||||
@@ -99,7 +98,7 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
}
|
||||
}
|
||||
|
||||
let args = yargParser(argv, {
|
||||
const args = yargParser(argv, {
|
||||
boolean: booleanFlags,
|
||||
alias: aliases,
|
||||
string: ['_'],
|
||||
@@ -115,8 +114,8 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
}
|
||||
}
|
||||
|
||||
let argOptions = {};
|
||||
for (let key in args) {
|
||||
const argOptions = {};
|
||||
for (const key in args) {
|
||||
if (!args.hasOwnProperty(key)) continue;
|
||||
if (key == '_') continue;
|
||||
argOptions[key] = args[key];
|
||||
@@ -125,27 +124,27 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
output.options = argOptions;
|
||||
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
cliUtils.promptMcq = function(message, answers) {
|
||||
const readline = require('readline');
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
message += "\n\n";
|
||||
for (let n in answers) {
|
||||
message += '\n\n';
|
||||
for (const n in answers) {
|
||||
if (!answers.hasOwnProperty(n)) continue;
|
||||
message += _('%s: %s', n, answers[n]) + "\n";
|
||||
message += `${_('%s: %s', n, answers[n])}\n`;
|
||||
}
|
||||
|
||||
message += "\n";
|
||||
message += '\n';
|
||||
message += _('Your choice: ');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
rl.question(message, (answer) => {
|
||||
rl.question(message, answer => {
|
||||
rl.close();
|
||||
|
||||
if (!(answer in answers)) {
|
||||
@@ -156,7 +155,7 @@ cliUtils.promptMcq = function(message, answers) {
|
||||
resolve(answer);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
cliUtils.promptConfirm = function(message, answers = null) {
|
||||
if (!answers) answers = [_('Y'), _('n')];
|
||||
@@ -164,23 +163,24 @@ cliUtils.promptConfirm = function(message, answers = null) {
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
message += ' (' + answers.join('/') + ')';
|
||||
message += ` (${answers.join('/')})`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
rl.question(message + ' ', (answer) => {
|
||||
return new Promise((resolve) => {
|
||||
rl.question(`${message} `, answer => {
|
||||
const ok = !answer || answer.toLowerCase() == answers[0].toLowerCase();
|
||||
rl.close();
|
||||
resolve(ok);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Note: initialText is there to have the same signature as statusBar.prompt() so that
|
||||
// it can be a drop-in replacement, however initialText is not used (and cannot be
|
||||
// with readline.question?).
|
||||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
cliUtils.prompt = function(initialText = '', promptString = ':', options = null) {
|
||||
if (!options) options = {};
|
||||
|
||||
@@ -189,10 +189,9 @@ cliUtils.prompt = function(initialText = '', promptString = ':', options = null)
|
||||
|
||||
const mutableStdout = new Writable({
|
||||
write: function(chunk, encoding, callback) {
|
||||
if (!this.muted)
|
||||
process.stdout.write(chunk, encoding);
|
||||
if (!this.muted) process.stdout.write(chunk, encoding);
|
||||
callback();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const rl = readline.createInterface({
|
||||
@@ -201,18 +200,18 @@ cliUtils.prompt = function(initialText = '', promptString = ':', options = null)
|
||||
terminal: true,
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
mutableStdout.muted = false;
|
||||
|
||||
rl.question(promptString, (answer) => {
|
||||
rl.question(promptString, answer => {
|
||||
rl.close();
|
||||
if (!!options.secure) this.stdout_('');
|
||||
if (options.secure) this.stdout_('');
|
||||
resolve(answer);
|
||||
});
|
||||
|
||||
mutableStdout.muted = !!options.secure;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let redrawStarted_ = false;
|
||||
let redrawLastLog_ = null;
|
||||
@@ -220,7 +219,7 @@ let redrawLastUpdateTime_ = 0;
|
||||
|
||||
cliUtils.setStdout = function(v) {
|
||||
this.stdout_ = v;
|
||||
}
|
||||
};
|
||||
|
||||
cliUtils.redraw = function(s) {
|
||||
const now = time.unixMs();
|
||||
@@ -233,8 +232,8 @@ cliUtils.redraw = function(s) {
|
||||
redrawLastLog_ = s;
|
||||
}
|
||||
|
||||
redrawStarted_ = true;
|
||||
}
|
||||
redrawStarted_ = true;
|
||||
};
|
||||
|
||||
cliUtils.redrawDone = function() {
|
||||
if (!redrawStarted_) return;
|
||||
@@ -245,6 +244,19 @@ cliUtils.redrawDone = function() {
|
||||
|
||||
redrawLastLog_ = null;
|
||||
redrawStarted_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { cliUtils };
|
||||
cliUtils.stdoutLogger = function(stdout) {
|
||||
const stdoutFn = (...s) => stdout(s.join(' '));
|
||||
|
||||
const logger = new Logger();
|
||||
logger.addTarget('console', { console: {
|
||||
info: stdoutFn,
|
||||
warn: stdoutFn,
|
||||
error: stdoutFn,
|
||||
} });
|
||||
|
||||
return logger;
|
||||
};
|
||||
|
||||
module.exports = { cliUtils };
|
||||
|
@@ -1,19 +1,12 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { toTitleCase } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const markdownUtils = require('lib/markdownUtils');
|
||||
const { Database } = require('lib/database.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'apidoc';
|
||||
}
|
||||
@@ -22,19 +15,27 @@ class Command extends BaseCommand {
|
||||
return 'Build the API doc';
|
||||
}
|
||||
|
||||
createPropertiesTable(tableFields) {
|
||||
const headers = [
|
||||
{ name: 'name', label: 'Name' },
|
||||
{ name: 'type', label: 'Type', filter: (value) => {
|
||||
return Database.enumName('fieldType', value);
|
||||
}},
|
||||
{ name: 'description', label: 'Description' },
|
||||
];
|
||||
|
||||
return markdownUtils.createMarkdownTable(headers, tableFields);
|
||||
enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
createPropertiesTable(tableFields) {
|
||||
const headers = [
|
||||
{ name: 'name', label: 'Name' },
|
||||
{
|
||||
name: 'type',
|
||||
label: 'Type',
|
||||
filter: value => {
|
||||
return Database.enumName('fieldType', value);
|
||||
},
|
||||
},
|
||||
{ name: 'description', label: 'Description' },
|
||||
];
|
||||
|
||||
return markdownUtils.createMarkdownTable(headers, tableFields);
|
||||
}
|
||||
|
||||
async action() {
|
||||
const models = [
|
||||
{
|
||||
type: BaseModel.TYPE_NOTE,
|
||||
@@ -55,7 +56,6 @@ class Command extends BaseCommand {
|
||||
lines.push('# Joplin API');
|
||||
lines.push('');
|
||||
|
||||
lines.push('When the Web Clipper service is enabled, Joplin exposes a [REST API](https://en.wikipedia.org/wiki/Representational_state_transfer) which allows third-party applications to access Joplin\'s data and to create, modify or delete notes, notebooks, resources or tags.');
|
||||
lines.push('');
|
||||
lines.push('In order to use it, you\'ll first need to find on which port the service is running. To do so, open the Web Clipper Options in Joplin and if the service is running it should tell you on which port. Normally it runs on port **41184**. If you want to find it programmatically, you may follow this kind of algorithm:');
|
||||
lines.push('');
|
||||
@@ -70,8 +70,8 @@ class Command extends BaseCommand {
|
||||
lines.push('}');
|
||||
lines.push('```');
|
||||
lines.push('');
|
||||
|
||||
lines.push('# Authorisation')
|
||||
|
||||
lines.push('# Authorisation');
|
||||
lines.push('');
|
||||
lines.push('To prevent unauthorised applications from accessing the API, the calls must be authentified. To do so, you must provide a token as a query parameter for each API call. You can get this token from the Joplin desktop application, on the Web Clipper Options screen.');
|
||||
lines.push('');
|
||||
@@ -113,6 +113,11 @@ class Command extends BaseCommand {
|
||||
lines.push('\tcurl http://localhost:41184/tags?fields=id');
|
||||
lines.push('');
|
||||
|
||||
lines.push('# Error handling');
|
||||
lines.push('');
|
||||
lines.push('In case of an error, an HTTP status code >= 400 will be returned along with a JSON object that provides more info about the error. The JSON object is in the format `{ "error": "description of error" }`.');
|
||||
lines.push('');
|
||||
|
||||
lines.push('# About the property types');
|
||||
lines.push('');
|
||||
lines.push('* Text is UTF-8.');
|
||||
@@ -125,6 +130,29 @@ class Command extends BaseCommand {
|
||||
lines.push('Call **GET /ping** to check if the service is available. It should return "JoplinClipperServer" if it works.');
|
||||
lines.push('');
|
||||
|
||||
lines.push('# Searching');
|
||||
lines.push('');
|
||||
lines.push('Call **GET /search?query=YOUR_QUERY** to search for notes. This end-point supports the `field` parameter which is recommended to use so that you only get the data that you need. The query syntax is as described in the main documentation: https://joplinapp.org/#searching');
|
||||
lines.push('');
|
||||
lines.push('To retrieve non-notes items, such as notebooks or tags, add a `type` parameter and set it to the required [item type name](#item-type-id). In that case, full text search will not be used - instead it will be a simple case-insensitive search. You can also use `*` as a wildcard. This is convenient for example to retrieve notebooks or tags by title.');
|
||||
lines.push('');
|
||||
lines.push('For example, to retrieve the notebook named `recipes`: **GET /search?query=recipes&type=folder**');
|
||||
lines.push('');
|
||||
lines.push('To retrieve all the tags that start with `project-`: **GET /search?query=project-*&type=tag**');
|
||||
lines.push('');
|
||||
|
||||
lines.push('# Item type IDs');
|
||||
lines.push('');
|
||||
lines.push('Item type IDs might be refered to in certain object you will retrieve from the API. This is the correspondance between name and ID:');
|
||||
lines.push('');
|
||||
lines.push('Name | Value');
|
||||
lines.push('---- | -----');
|
||||
for (const t of BaseModel.typeEnum_) {
|
||||
const value = t[1];
|
||||
lines.push(`${BaseModel.modelTypeToName(value)} | ${value} `);
|
||||
}
|
||||
lines.push('');
|
||||
|
||||
for (let i = 0; i < models.length; i++) {
|
||||
const model = models[i];
|
||||
const ModelClass = BaseItem.getClassByItemType(model.type);
|
||||
@@ -154,14 +182,14 @@ class Command extends BaseCommand {
|
||||
type: Database.enumId('fieldType', 'text'),
|
||||
description: 'If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format `{ x: x, y: y, width: width, height: height }`',
|
||||
});
|
||||
tableFields.push({
|
||||
name: 'tags',
|
||||
type: Database.enumId('fieldType', 'text'),
|
||||
description: 'Comma-separated list of tags. eg. `tag1,tag2`.',
|
||||
});
|
||||
// tableFields.push({
|
||||
// name: 'tags',
|
||||
// type: Database.enumId('fieldType', 'text'),
|
||||
// description: 'Comma-separated list of tags. eg. `tag1,tag2`.',
|
||||
// });
|
||||
}
|
||||
|
||||
lines.push('# ' + toTitleCase(tableName));
|
||||
lines.push(`# ${toTitleCase(tableName)}`);
|
||||
lines.push('');
|
||||
|
||||
if (model.type === BaseModel.TYPE_FOLDER) {
|
||||
@@ -174,9 +202,9 @@ class Command extends BaseCommand {
|
||||
lines.push(this.createPropertiesTable(tableFields));
|
||||
lines.push('');
|
||||
|
||||
lines.push('## GET /' + tableName);
|
||||
lines.push(`## GET /${tableName}`);
|
||||
lines.push('');
|
||||
lines.push('Gets all ' + tableName);
|
||||
lines.push(`Gets all ${tableName}`);
|
||||
lines.push('');
|
||||
|
||||
if (model.type === BaseModel.TYPE_FOLDER) {
|
||||
@@ -184,9 +212,9 @@ class Command extends BaseCommand {
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
lines.push('## GET /' + tableName + '/:id');
|
||||
lines.push(`## GET /${tableName}/:id`);
|
||||
lines.push('');
|
||||
lines.push('Gets ' + singular + ' with ID :id');
|
||||
lines.push(`Gets ${singular} with ID :id`);
|
||||
lines.push('');
|
||||
|
||||
if (model.type === BaseModel.TYPE_TAG) {
|
||||
@@ -201,6 +229,11 @@ class Command extends BaseCommand {
|
||||
lines.push('');
|
||||
lines.push('Gets all the tags attached to this note.');
|
||||
lines.push('');
|
||||
|
||||
lines.push('## GET /notes/:id/resources');
|
||||
lines.push('');
|
||||
lines.push('Gets all the resources attached to this note.');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
if (model.type === BaseModel.TYPE_FOLDER) {
|
||||
@@ -217,9 +250,9 @@ class Command extends BaseCommand {
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
lines.push('## POST /' + tableName);
|
||||
lines.push(`## POST /${tableName}`);
|
||||
lines.push('');
|
||||
lines.push('Creates a new ' + singular);
|
||||
lines.push(`Creates a new ${singular}`);
|
||||
lines.push('');
|
||||
|
||||
if (model.type === BaseModel.TYPE_RESOURCE) {
|
||||
@@ -257,20 +290,20 @@ class Command extends BaseCommand {
|
||||
lines.push('');
|
||||
lines.push('### Creating a note with a specific ID');
|
||||
lines.push('');
|
||||
lines.push('When a new note is created, it is automatically assigned a new unique ID so **normally you do not need to set the ID**. However, if for some reason you want to set it, you can supply it as the `id` property. It needs to be a 32 characters long hexadecimal string. **Make sure it is unique**, for example by generating it using whatever GUID function is available in your programming language.');
|
||||
lines.push('When a new note is created, it is automatically assigned a new unique ID so **normally you do not need to set the ID**. However, if for some reason you want to set it, you can supply it as the `id` property. It needs to be a **32 characters long string** in hexadecimal. **Make sure it is unique**, for example by generating it using whatever GUID function is available in your programming language.');
|
||||
lines.push('');
|
||||
lines.push(' curl --data \'{ "id": "00a87474082744c1a8515da6aa5792d2", "title": "My note with custom ID"}\' http://127.0.0.1:41184/notes');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
lines.push('## PUT /' + tableName + '/:id');
|
||||
lines.push(`## PUT /${tableName}/:id`);
|
||||
lines.push('');
|
||||
lines.push('Sets the properties of the ' + singular + ' with ID :id');
|
||||
lines.push(`Sets the properties of the ${singular} with ID :id`);
|
||||
lines.push('');
|
||||
|
||||
lines.push('## DELETE /' + tableName + '/:id');
|
||||
lines.push(`## DELETE /${tableName}/:id`);
|
||||
lines.push('');
|
||||
lines.push('Deletes the ' + singular + ' with ID :id');
|
||||
lines.push(`Deletes the ${singular} with ID :id`);
|
||||
lines.push('');
|
||||
|
||||
if (model.type === BaseModel.TYPE_TAG) {
|
||||
@@ -283,7 +316,6 @@ class Command extends BaseCommand {
|
||||
|
||||
this.stdout(lines.join('\n'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
|
@@ -3,10 +3,8 @@ const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'attach <note> <file>';
|
||||
}
|
||||
@@ -16,9 +14,9 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let title = args['note'];
|
||||
const title = args['note'];
|
||||
|
||||
let note = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
||||
const note = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
||||
this.encryptionCheck(note);
|
||||
if (!note) throw new Error(_('Cannot find "%s".', title));
|
||||
|
||||
@@ -26,7 +24,6 @@ class Command extends BaseCommand {
|
||||
|
||||
await shim.attachFileToNote(note, localFilePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
|
@@ -2,11 +2,10 @@ const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'cat <note>';
|
||||
}
|
||||
@@ -16,24 +15,27 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
options() {
|
||||
return [
|
||||
['-v, --verbose', _('Displays the complete information about note.')],
|
||||
];
|
||||
return [['-v, --verbose', _('Displays the complete information about note.')]];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let title = args['note'];
|
||||
const title = args['note'];
|
||||
|
||||
let item = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
||||
const item = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
||||
if (!item) throw new Error(_('Cannot find "%s".', title));
|
||||
|
||||
const content = args.options.verbose ? await Note.serialize(item) : await Note.serializeForEdit(item);
|
||||
this.stdout(content);
|
||||
let content = '';
|
||||
|
||||
if (item.encryption_applied) {
|
||||
content = BaseItem.displayTitle(item);
|
||||
} else {
|
||||
content = args.options.verbose ? await Note.serialize(item) : await Note.serializeForEdit(item);
|
||||
}
|
||||
|
||||
this.stdout(content);
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
|
@@ -1,28 +1,75 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _, setLocale } = require('lib/locale.js');
|
||||
const { app } = require('./app.js');
|
||||
const fs = require('fs-extra');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'config [name] [value]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Gets or sets a config value. If [value] is not provided, it will show the value of [name]. If neither [name] nor [value] is provided, it will list the current configuration.");
|
||||
return _('Gets or sets a config value. If [value] is not provided, it will show the value of [name]. If neither [name] nor [value] is provided, it will list the current configuration.');
|
||||
}
|
||||
|
||||
options() {
|
||||
return [
|
||||
['-v, --verbose', _('Also displays unset and hidden config variables.')],
|
||||
['--export', 'Writes all settings to STDOUT as JSON including secure variables.'],
|
||||
['--import', 'Reads in JSON formatted settings from STDIN.'],
|
||||
['--import-file <file>', 'Reads in settings from <file>. <file> must contain valid JSON.'],
|
||||
];
|
||||
}
|
||||
async __importSettings(inputStream) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// being defensive and not attempting to settle twice
|
||||
let isSettled = false;
|
||||
const chunks = [];
|
||||
|
||||
inputStream.on('readable', () => {
|
||||
let chunk;
|
||||
while ((chunk = inputStream.read()) !== null) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
});
|
||||
|
||||
inputStream.on('end', () => {
|
||||
const json = chunks.join('');
|
||||
let settingsObj;
|
||||
try {
|
||||
settingsObj = JSON.parse(json);
|
||||
} catch (err) {
|
||||
isSettled = true;
|
||||
return reject(new Error(`Invalid JSON passed to config --import: \n${err.message}.`));
|
||||
}
|
||||
if (settingsObj) {
|
||||
Object.entries(settingsObj)
|
||||
.forEach(([key, value]) => {
|
||||
Setting.setValue(key, value);
|
||||
});
|
||||
}
|
||||
if (!isSettled) {
|
||||
isSettled = true;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
inputStream.on('error', (error) => {
|
||||
if (!isSettled) {
|
||||
isSettled = true;
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
async action(args) {
|
||||
const verbose = args.options.verbose;
|
||||
const isExport = args.options.export;
|
||||
const isImport = args.options.import || args.options.importFile;
|
||||
const importFile = args.options.importFile;
|
||||
|
||||
const renderKeyValue = (name) => {
|
||||
const renderKeyValue = name => {
|
||||
const md = Setting.settingMetadata(name);
|
||||
let value = Setting.value(name);
|
||||
if (typeof value === 'object' || Array.isArray(value)) value = JSON.stringify(value);
|
||||
@@ -33,29 +80,47 @@ class Command extends BaseCommand {
|
||||
} else {
|
||||
return _('%s = %s', name, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!args.name && !args.value) {
|
||||
let keys = Setting.keys(!verbose, 'cli');
|
||||
if (isExport || (!isImport && !args.value)) {
|
||||
const keys = Setting.keys(!verbose, 'cli');
|
||||
keys.sort();
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const value = Setting.value(keys[i]);
|
||||
if (!verbose && !value) continue;
|
||||
this.stdout(renderKeyValue(keys[i]));
|
||||
|
||||
if (isExport) {
|
||||
const resultObj = keys.reduce((acc, key) => {
|
||||
const value = Setting.value(key);
|
||||
if (!verbose && !value) return acc;
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {});
|
||||
// Printing the object in "pretty" format so it's easy to read/edit
|
||||
this.stdout(JSON.stringify(resultObj, null, 2));
|
||||
} else if (!args.name) {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const value = Setting.value(keys[i]);
|
||||
if (!verbose && !value) continue;
|
||||
this.stdout(renderKeyValue(keys[i]));
|
||||
}
|
||||
} else {
|
||||
this.stdout(renderKeyValue(args.name));
|
||||
}
|
||||
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.name && !args.value) {
|
||||
this.stdout(renderKeyValue(args.name));
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
return;
|
||||
if (isImport) {
|
||||
let fileStream = process.stdin;
|
||||
if (importFile) {
|
||||
fileStream = fs.createReadStream(importFile, { autoClose: true });
|
||||
}
|
||||
await this.__importSettings(fileStream);
|
||||
} else {
|
||||
Setting.setValue(args.name, args.value);
|
||||
}
|
||||
|
||||
Setting.setValue(args.name, args.value);
|
||||
|
||||
if (args.name == 'locale') {
|
||||
setLocale(Setting.value('locale'));
|
||||
@@ -64,7 +129,6 @@ class Command extends BaseCommand {
|
||||
|
||||
await Setting.saveAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
|
@@ -2,11 +2,9 @@ const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'cp <note> [notebook]';
|
||||
}
|
||||
@@ -33,7 +31,6 @@ class Command extends BaseCommand {
|
||||
Note.updateGeolocation(newNote.id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
|
@@ -2,12 +2,10 @@ const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'done <note>';
|
||||
}
|
||||
@@ -35,7 +33,6 @@ class Command extends BaseCommand {
|
||||
async action(args) {
|
||||
await Command.handleAction(this, args, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
|
@@ -1,12 +1,9 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'dump';
|
||||
}
|
||||
@@ -19,26 +16,25 @@ class Command extends BaseCommand {
|
||||
return true;
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
async action() {
|
||||
let items = [];
|
||||
let folders = await Folder.all();
|
||||
const folders = await Folder.all();
|
||||
for (let i = 0; i < folders.length; i++) {
|
||||
let folder = folders[i];
|
||||
let notes = await Note.previews(folder.id);
|
||||
const folder = folders[i];
|
||||
const notes = await Note.previews(folder.id);
|
||||
items.push(folder);
|
||||
items = items.concat(notes);
|
||||
}
|
||||
|
||||
let tags = await Tag.all();
|
||||
const tags = await Tag.all();
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
tags[i].notes_ = await Tag.noteIds(tags[i].id);
|
||||
}
|
||||
|
||||
items = items.concat(tags);
|
||||
|
||||
|
||||
this.stdout(JSON.stringify(items));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
|
@@ -1,20 +1,21 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const pathUtils = require('lib/path-utils.js');
|
||||
const imageType = require('image-type');
|
||||
const readChunk = require('read-chunk');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'e2ee <command> [path]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _('Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, `status` and `target-status`.');
|
||||
return _('Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, `status`, `decrypt-file` and `target-status`.');
|
||||
}
|
||||
|
||||
options() {
|
||||
@@ -22,21 +23,66 @@ class Command extends BaseCommand {
|
||||
// This is here mostly for testing - shouldn't be used
|
||||
['-p, --password <password>', 'Use this password as master password (For security reasons, it is not recommended to use this option).'],
|
||||
['-v, --verbose', 'More verbose output for the `target-status` command'],
|
||||
['-o, --output <directory>', 'Output directory'],
|
||||
['--retry-failed-items', 'Applies to `decrypt` command - retries decrypting items that previously could not be decrypted.'],
|
||||
];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
// change-password
|
||||
|
||||
const options = args.options;
|
||||
|
||||
const askForMasterKey = async error => {
|
||||
const masterKeyId = error.masterKeyId;
|
||||
const password = await this.prompt(_('Enter master password:'), { type: 'string', secure: true });
|
||||
if (!password) {
|
||||
this.stdout(_('Operation cancelled'));
|
||||
return false;
|
||||
}
|
||||
Setting.setObjectKey('encryption.passwordCache', masterKeyId, password);
|
||||
await EncryptionService.instance().loadMasterKeysFromSettings();
|
||||
return true;
|
||||
};
|
||||
|
||||
const startDecryption = async () => {
|
||||
this.stdout(_('Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt.'));
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
const result = await DecryptionWorker.instance().start();
|
||||
const line = [];
|
||||
line.push(_('Decrypted items: %d', result.decryptedItemCount));
|
||||
if (result.skippedItemCount) line.push(_('Skipped items: %d (use --retry-failed-items to retry decrypting them)', result.skippedItemCount));
|
||||
this.stdout(line.join('\n'));
|
||||
break;
|
||||
} catch (error) {
|
||||
if (error.code === 'masterKeyNotLoaded') {
|
||||
const ok = await askForMasterKey(error);
|
||||
if (!ok) return;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
this.stdout(_('Completed decryption.'));
|
||||
};
|
||||
|
||||
if (args.command === 'enable') {
|
||||
const password = options.password ? options.password.toString() : await this.prompt(_('Enter master password:'), { type: 'string', secure: true });
|
||||
if (!password) {
|
||||
this.stdout(_('Operation cancelled'));
|
||||
return;
|
||||
}
|
||||
|
||||
const password2 = await this.prompt(_('Confirm password:'), { type: 'string', secure: true });
|
||||
if (!password2) {
|
||||
this.stdout(_('Operation cancelled'));
|
||||
return;
|
||||
}
|
||||
if (password !== password2) {
|
||||
this.stdout(_('Passwords do not match!'));
|
||||
return;
|
||||
}
|
||||
await EncryptionService.instance().generateMasterKeyAndEnableEncryption(password);
|
||||
return;
|
||||
}
|
||||
@@ -47,63 +93,12 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
if (args.command === 'decrypt') {
|
||||
while (true) {
|
||||
try {
|
||||
if (args.path) {
|
||||
const plainText = await EncryptionService.instance().decryptString(args.path);
|
||||
this.stdout(plainText);
|
||||
return;
|
||||
} else {
|
||||
if (process.stdin.isTTY) {
|
||||
this.stdout(_('Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt.'));
|
||||
await DecryptionWorker.instance().start();
|
||||
this.stdout(_('Completed decryption.'));
|
||||
return;
|
||||
} else {
|
||||
// var repl = require("repl");
|
||||
// var r = repl.start("node> ");
|
||||
|
||||
const text = await new Promise((accept, reject) => {
|
||||
var buffer = '';
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', function(chunk) {
|
||||
buffer += chunk;
|
||||
// process.stdout.write(chunk);
|
||||
});
|
||||
process.stdin.on('end', function() {
|
||||
accept(buffer.trim());
|
||||
});
|
||||
});
|
||||
|
||||
if (text.length > 0) {
|
||||
var cipherText = text;
|
||||
try {
|
||||
var item = await BaseItem.unserialize(text);
|
||||
cipherText = item.encryption_cipher_text;
|
||||
} catch (error) {
|
||||
// we already got the pure cipher text
|
||||
}
|
||||
const plainText = await EncryptionService.instance().decryptString(cipherText);
|
||||
this.stdout(plainText);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === 'masterKeyNotLoaded') {
|
||||
const masterKeyId = error.masterKeyId;
|
||||
const password = await this.prompt(_('Enter master password:'), { type: 'string', secure: true });
|
||||
if (!password) {
|
||||
this.stdout(_('Operation cancelled'));
|
||||
return;
|
||||
}
|
||||
Setting.setObjectKey('encryption.passwordCache', masterKeyId, password);
|
||||
await EncryptionService.instance().loadMasterKeysFromSettings();
|
||||
continue;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
if (args.path) {
|
||||
const plainText = await EncryptionService.instance().decryptString(args.path);
|
||||
this.stdout(plainText);
|
||||
} else {
|
||||
if (args.options['retry-failed-items']) await DecryptionWorker.instance().clearDisabledItems();
|
||||
await startDecryption();
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -114,21 +109,49 @@ class Command extends BaseCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.command === 'decrypt-file') {
|
||||
while (true) {
|
||||
try {
|
||||
const outputDir = options.output ? options.output : require('os').tmpdir();
|
||||
let outFile = `${outputDir}/${pathUtils.filename(args.path)}.${Date.now()}.bin`;
|
||||
await EncryptionService.instance().decryptFile(args.path, outFile);
|
||||
const buffer = await readChunk(outFile, 0, 64);
|
||||
const detectedType = imageType(buffer);
|
||||
|
||||
if (detectedType) {
|
||||
const newOutFile = `${outFile}.${detectedType.ext}`;
|
||||
await shim.fsDriver().move(outFile, newOutFile);
|
||||
outFile = newOutFile;
|
||||
}
|
||||
|
||||
this.stdout(outFile);
|
||||
break;
|
||||
} catch (error) {
|
||||
if (error.code === 'masterKeyNotLoaded') {
|
||||
const ok = await askForMasterKey(error);
|
||||
if (!ok) return;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.command === 'target-status') {
|
||||
const fs = require('fs-extra');
|
||||
const pathUtils = require('lib/path-utils.js');
|
||||
const fsDriver = new (require('lib/fs-driver-node.js').FsDriverNode)();
|
||||
|
||||
const targetPath = args.path;
|
||||
if (!targetPath) throw new Error('Please specify the sync target path.');
|
||||
|
||||
const dirPaths = function(targetPath) {
|
||||
let paths = [];
|
||||
fs.readdirSync(targetPath).forEach((path) => {
|
||||
const paths = [];
|
||||
fs.readdirSync(targetPath).forEach(path => {
|
||||
paths.push(path);
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
};
|
||||
|
||||
let itemCount = 0;
|
||||
let resourceCount = 0;
|
||||
@@ -136,24 +159,24 @@ class Command extends BaseCommand {
|
||||
let encryptedResourceCount = 0;
|
||||
let otherItemCount = 0;
|
||||
|
||||
let encryptedPaths = [];
|
||||
let decryptedPaths = [];
|
||||
const encryptedPaths = [];
|
||||
const decryptedPaths = [];
|
||||
|
||||
let paths = dirPaths(targetPath);
|
||||
const paths = dirPaths(targetPath);
|
||||
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const path = paths[i];
|
||||
const fullPath = targetPath + '/' + path;
|
||||
const fullPath = `${targetPath}/${path}`;
|
||||
const stat = await fs.stat(fullPath);
|
||||
|
||||
// this.stdout(fullPath);
|
||||
|
||||
if (path === '.resource') {
|
||||
let resourcePaths = dirPaths(fullPath);
|
||||
const resourcePaths = dirPaths(fullPath);
|
||||
for (let j = 0; j < resourcePaths.length; j++) {
|
||||
const resourcePath = resourcePaths[j];
|
||||
resourceCount++;
|
||||
const fullResourcePath = fullPath + '/' + resourcePath;
|
||||
const fullResourcePath = `${fullPath}/${resourcePath}`;
|
||||
const isEncrypted = await EncryptionService.instance().fileIsEncrypted(fullResourcePath);
|
||||
if (isEncrypted) {
|
||||
encryptedResourceCount++;
|
||||
@@ -187,9 +210,9 @@ class Command extends BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
this.stdout('Encrypted items: ' + encryptedItemCount + '/' + itemCount);
|
||||
this.stdout('Encrypted resources: ' + encryptedResourceCount + '/' + resourceCount);
|
||||
this.stdout('Other items (never encrypted): ' + otherItemCount);
|
||||
this.stdout(`Encrypted items: ${encryptedItemCount}/${itemCount}`);
|
||||
this.stdout(`Encrypted resources: ${encryptedResourceCount}/${resourceCount}`);
|
||||
this.stdout(`Other items (never encrypted): ${otherItemCount}`);
|
||||
|
||||
if (options.verbose) {
|
||||
this.stdout('');
|
||||
@@ -212,7 +235,6 @@ class Command extends BaseCommand {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
|
@@ -1,17 +1,14 @@
|
||||
const fs = require('fs-extra');
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { splitCommandString } = require('lib/string-utils.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'edit <note>';
|
||||
}
|
||||
@@ -21,25 +18,24 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let watcher = null;
|
||||
let tempFilePath = null;
|
||||
|
||||
const onFinishedEditing = async () => {
|
||||
if (tempFilePath) fs.removeSync(tempFilePath);
|
||||
}
|
||||
};
|
||||
|
||||
const textEditorPath = () => {
|
||||
if (Setting.value('editor')) return Setting.value('editor');
|
||||
if (process.env.EDITOR) return process.env.EDITOR;
|
||||
throw new Error(_('No text editor is defined. Please set it using `config editor <editor-path>`'));
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
try {
|
||||
// -------------------------------------------------------------------------
|
||||
// Load note or create it if it doesn't exist
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
let title = args['note'];
|
||||
const title = args['note'];
|
||||
|
||||
if (!app().currentFolder()) throw new Error(_('No active notebook.'));
|
||||
let note = await app().loadItem(BaseModel.TYPE_NOTE, title);
|
||||
@@ -58,14 +54,14 @@ class Command extends BaseCommand {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
let editorPath = textEditorPath();
|
||||
let editorArgs = editorPath.split(' ');
|
||||
let editorArgs = splitCommandString(editorPath);
|
||||
|
||||
editorPath = editorArgs[0];
|
||||
editorArgs = editorArgs.splice(1);
|
||||
|
||||
const originalContent = await Note.serializeForEdit(note);
|
||||
|
||||
tempFilePath = Setting.value('tempDir') + '/' + uuid.create() + '.md';
|
||||
tempFilePath = `${Setting.value('tempDir')}/${uuid.create()}.md`;
|
||||
editorArgs.push(tempFilePath);
|
||||
|
||||
await fs.writeFile(tempFilePath, originalContent);
|
||||
@@ -80,7 +76,7 @@ class Command extends BaseCommand {
|
||||
await app().gui().forceRender();
|
||||
const termState = app().gui().termSaveState();
|
||||
|
||||
const spawnSync = require('child_process').spawnSync;
|
||||
const spawnSync = require('child_process').spawnSync;
|
||||
const result = spawnSync(editorPath, editorArgs, { stdio: 'inherit' });
|
||||
|
||||
if (result.error) this.stdout(_('Error opening note in editor: %s', result.error.message));
|
||||
@@ -95,7 +91,7 @@ class Command extends BaseCommand {
|
||||
|
||||
const updatedContent = await fs.readFile(tempFilePath, 'utf8');
|
||||
if (updatedContent !== originalContent) {
|
||||
let updatedNote = await Note.unserializeForEdit(updatedContent);
|
||||
const updatedNote = await Note.unserializeForEdit(updatedContent);
|
||||
updatedNote.id = note.id;
|
||||
await Note.save(updatedNote);
|
||||
this.stdout(_('Note has been saved.'));
|
||||
@@ -107,13 +103,11 @@ class Command extends BaseCommand {
|
||||
});
|
||||
|
||||
await onFinishedEditing();
|
||||
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
await onFinishedEditing();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
|
@@ -3,7 +3,6 @@ const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'exit';
|
||||
}
|
||||
@@ -16,10 +15,9 @@ class Command extends BaseCommand {
|
||||
return ['gui'];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
async action() {
|
||||
await app().exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
|
@@ -1,13 +1,10 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { app } = require('./app.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'export-sync-status';
|
||||
}
|
||||
@@ -20,17 +17,20 @@ class Command extends BaseCommand {
|
||||
return true;
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
async action() {
|
||||
const service = new ReportService();
|
||||
const csv = await service.basicItemList({ format: 'csv' });
|
||||
const filePath = Setting.value('profileDir') + '/syncReport-' + (new Date()).getTime() + '.csv';
|
||||
const filePath = `${Setting.value('profileDir')}/syncReport-${new Date().getTime()}.csv`;
|
||||
await fs.writeFileSync(filePath, csv);
|
||||
this.stdout('Sync status exported to ' + filePath);
|
||||
this.stdout(`Sync status exported to ${filePath}`);
|
||||
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
app()
|
||||
.gui()
|
||||
.maximizeConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
|