Compare commits
1209 Commits
pre-monore
...
plus-theme
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7d096e768 | ||
|
|
396f737612 | ||
|
|
2dd8d5a119 | ||
|
|
656c5b61cc | ||
|
|
9fd0f6834a | ||
|
|
58dac27186 | ||
|
|
c8befb6843 | ||
|
|
fafd47fd90 | ||
|
|
e0990910f6 | ||
|
|
599f23c8aa | ||
|
|
a0bd011b80 | ||
|
|
157c27c20d | ||
|
|
4d0407a3b3 | ||
|
|
f73ed1213f | ||
|
|
daabe11d11 | ||
|
|
aaf125cfca | ||
|
|
161fc6bb68 | ||
|
|
1afed9f256 | ||
|
|
e8d6c15fee | ||
|
|
8686b28c28 | ||
|
|
d68251ad4b | ||
|
|
a02b4ae08f | ||
|
|
9ed1dd714b | ||
|
|
95dc6c66d9 | ||
|
|
e91c76c10c | ||
|
|
9deddbbe7c | ||
|
|
2d416d491c | ||
|
|
a19ce0458a | ||
|
|
3fca24e6fd | ||
|
|
6f956e0423 | ||
|
|
bb3ce9fbc6 | ||
|
|
06f2340569 | ||
|
|
510c29cc71 | ||
|
|
807aa04807 | ||
|
|
c84e2e0537 | ||
|
|
910e219c0e | ||
|
|
3a4843fb46 | ||
|
|
1b3744baa2 | ||
|
|
c7d8c8b104 | ||
|
|
a380d39865 | ||
|
|
34d3310eae | ||
|
|
f328eec26d | ||
|
|
7684a54678 | ||
|
|
51f3174609 | ||
|
|
4fcd518b0a | ||
|
|
1e0d7c7e28 | ||
|
|
984da93a7b | ||
|
|
c2c7eda799 | ||
|
|
04b85630b9 | ||
|
|
ae4f3759c2 | ||
|
|
a238dcc5e6 | ||
|
|
57ba84107c | ||
|
|
ca913cafbc | ||
|
|
7032421096 | ||
|
|
06ac959496 | ||
|
|
41c15f8c96 | ||
|
|
5cad3dfbae | ||
|
|
49a20a303a | ||
|
|
90f74427d9 | ||
|
|
ba9dc6ce26 | ||
|
|
3a73994ad4 | ||
|
|
797e1f1f21 | ||
|
|
f1713647cf | ||
|
|
8704d3acb3 | ||
|
|
ce4250281f | ||
|
|
aecbc714c4 | ||
|
|
c20242cf1c | ||
|
|
04ba76aac8 | ||
|
|
d1bc65c266 | ||
|
|
dab284f339 | ||
|
|
df76d9a50a | ||
|
|
abec2e48d4 | ||
|
|
0f2ddb452c | ||
|
|
6fa1acc461 | ||
|
|
9502639447 | ||
|
|
ef4acb1b66 | ||
|
|
8cb72d306b | ||
|
|
1ef8bf4690 | ||
|
|
22779c9dbc | ||
|
|
b52467b6d6 | ||
|
|
d33a0cd589 | ||
|
|
a04cb54d86 | ||
|
|
10785f156f | ||
|
|
4736fb8e32 | ||
|
|
531e7ec73d | ||
|
|
ff946b4f2f | ||
|
|
68f0e68343 | ||
|
|
12a3520563 | ||
|
|
e84426a4f9 | ||
|
|
8b80934f77 | ||
|
|
b6618f81f8 | ||
|
|
bd49d6bd89 | ||
|
|
61fd6f1741 | ||
|
|
05d45d9aab | ||
|
|
612eb64a83 | ||
|
|
e0a9e62d84 | ||
|
|
5db5f2251f | ||
|
|
34fd9d29c8 | ||
|
|
c6d8476963 | ||
|
|
1d9fe0c03d | ||
|
|
5b2d36e976 | ||
|
|
547135f7d2 | ||
|
|
15f1416f52 | ||
|
|
b2709aa816 | ||
|
|
9775d4e74d | ||
|
|
f5661af1f4 | ||
|
|
611bec38b4 | ||
|
|
5a166d2455 | ||
|
|
6808d270a7 | ||
|
|
9dc68611a1 | ||
|
|
e932f38160 | ||
|
|
9014835880 | ||
|
|
00328d04cd | ||
|
|
5b1850e161 | ||
|
|
60727656c9 | ||
|
|
372d021062 | ||
|
|
9c4421bfe0 | ||
|
|
5f85af76cd | ||
|
|
4c2565826f | ||
|
|
ae2d83c8aa | ||
|
|
3b0216ca7e | ||
|
|
1abfb175b5 | ||
|
|
a771695348 | ||
|
|
fe3e4edb04 | ||
|
|
364a4faa75 | ||
|
|
331787fe43 | ||
|
|
9484a7237c | ||
|
|
96063969ae | ||
|
|
cb1c8be870 | ||
|
|
5ea71da181 | ||
|
|
dda469d10e | ||
|
|
9f6e033c53 | ||
|
|
15f44adf3b | ||
|
|
e9483cb242 | ||
|
|
5243d8bedf | ||
|
|
ed731fd7a6 | ||
|
|
07f5422132 | ||
|
|
5aa1764848 | ||
|
|
4973ee555b | ||
|
|
4bfccba4c0 | ||
|
|
5ddad8ebc2 | ||
|
|
b5921897d6 | ||
|
|
53a46b88b8 | ||
|
|
da977ccace | ||
|
|
7b9a2167ad | ||
|
|
fc16771355 | ||
|
|
3ef3fbfd0e | ||
|
|
415a0aaf75 | ||
|
|
5a4a9b570e | ||
|
|
2df60d6387 | ||
|
|
9c176013ab | ||
|
|
036d251e15 | ||
|
|
925060689a | ||
|
|
f0631d734e | ||
|
|
52b299315d | ||
|
|
0195e94aa7 | ||
|
|
75f0b2b82c | ||
|
|
f26f283c1f | ||
|
|
7c387b2aee | ||
|
|
071ecb284e | ||
|
|
03242942c2 | ||
|
|
a476927595 | ||
|
|
ec6e1704be | ||
|
|
d2f189607e | ||
|
|
c13777e68d | ||
|
|
2fb63dcfb1 | ||
|
|
0adb7685f6 | ||
|
|
1cbe99a0d8 | ||
|
|
fa83da6085 | ||
|
|
1392c16f12 | ||
|
|
d6393653e1 | ||
|
|
a75de51198 | ||
|
|
a21869ec9b | ||
|
|
f534e4ee37 | ||
|
|
9add661a5b | ||
|
|
81948a5c29 | ||
|
|
5924154a62 | ||
|
|
354bfe58cd | ||
|
|
c5974a8575 | ||
|
|
c0275addb0 | ||
|
|
c85baf7fd0 | ||
|
|
39e5778bd0 | ||
|
|
a3b77bb37f | ||
|
|
660cf0c1e4 | ||
|
|
11020c4545 | ||
|
|
e93bf02e3a | ||
|
|
a629d7f048 | ||
|
|
683b0f5c48 | ||
|
|
42a80a41ca | ||
|
|
9dcb90a9cf | ||
|
|
1af0aa3f57 | ||
|
|
35c9338220 | ||
|
|
2e8fa0f6c5 | ||
|
|
931aeda17f | ||
|
|
a037d24b0f | ||
|
|
1fa556cd00 | ||
|
|
82e9ad8a20 | ||
|
|
d893765b24 | ||
|
|
1108b0264e | ||
|
|
4f060579ca | ||
|
|
1940c6e1ba | ||
|
|
e754f6c234 | ||
|
|
d19dbbfe7e | ||
|
|
3a735ea0ce | ||
|
|
e319d19a54 | ||
|
|
5f075e4936 | ||
|
|
d6ba3f3adf | ||
|
|
b453e2cf1a | ||
|
|
2d14e5682d | ||
|
|
1f58aebb2b | ||
|
|
6d70ced93a | ||
|
|
accc53c5dd | ||
|
|
e4cb8b71dd | ||
|
|
f6eff090e7 | ||
|
|
61a2c362b9 | ||
|
|
cbcee037a7 | ||
|
|
de2e4a9c7f | ||
|
|
0ba0a5bcd8 | ||
|
|
70cc409402 | ||
|
|
160ca610ae | ||
|
|
2f1aa95f4c | ||
|
|
3eead128a6 | ||
|
|
4c22a73ff3 | ||
|
|
a78f58de92 | ||
|
|
fcda48903b | ||
|
|
3f9805ac56 | ||
|
|
2131fed0bb | ||
|
|
baa5d2fcac | ||
|
|
7cf9b76eb5 | ||
|
|
200648275e | ||
|
|
a7584d62c7 | ||
|
|
74e6caf633 | ||
|
|
f1f531cbfa | ||
|
|
b5a275ad07 | ||
|
|
5051ad91ff | ||
|
|
06d6cb6d19 | ||
|
|
92116273b0 | ||
|
|
6169ff99a2 | ||
|
|
67f77d027a | ||
|
|
39e1a803a1 | ||
|
|
ba159e1a3e | ||
|
|
a60b77121a | ||
|
|
810f0104be | ||
|
|
07489fb405 | ||
|
|
82f0bdfc18 | ||
|
|
66154b30c1 | ||
|
|
bf5d391d53 | ||
|
|
591ce0894e | ||
|
|
1c18563dfb | ||
|
|
b60e781767 | ||
|
|
ca54544b82 | ||
|
|
9c3ff71ce4 | ||
|
|
fd82ec7659 | ||
|
|
23ccaacdee | ||
|
|
39a4297168 | ||
|
|
544111846c | ||
|
|
79bdea0441 | ||
|
|
c056c4e79e | ||
|
|
19828e9070 | ||
|
|
8231c736ca | ||
|
|
85f639b1f0 | ||
|
|
c471b6d66c | ||
|
|
a363efccfb | ||
|
|
871a17a108 | ||
|
|
16a39b364c | ||
|
|
91f79d2be0 | ||
|
|
9932fe5055 | ||
|
|
c296597427 | ||
|
|
97cb5bc12c | ||
|
|
7c596d6bf1 | ||
|
|
b68799e2d1 | ||
|
|
a3d4db9fc1 | ||
|
|
8369330053 | ||
|
|
46a6fee81d | ||
|
|
b2c75130ce | ||
|
|
ef444f2a6f | ||
|
|
36f0871f69 | ||
|
|
5adf27d345 | ||
|
|
5e4c261332 | ||
|
|
6fb04e23ad | ||
|
|
2c850a612d | ||
|
|
e555c4d083 | ||
|
|
cb32cca6aa | ||
|
|
ffb491a61f | ||
|
|
4ecba93a25 | ||
|
|
52ec1d09b8 | ||
|
|
3d2e2e2278 | ||
|
|
f9a9ece320 | ||
|
|
ffc46d6a48 | ||
|
|
ce458642af | ||
|
|
820d3ef426 | ||
|
|
145386b6a7 | ||
|
|
a708cf7f69 | ||
|
|
c70ebb6cce | ||
|
|
a260b6eac4 | ||
|
|
0ab17e1ec8 | ||
|
|
d1a0cd1173 | ||
|
|
3955b973ef | ||
|
|
fcfa508cbc | ||
|
|
a6766ef1c0 | ||
|
|
606b5ac5ab | ||
|
|
9acc463c7f | ||
|
|
c4ddd08b0a | ||
|
|
5c7dd10769 | ||
|
|
3fa86cb441 | ||
|
|
4a74ee0d72 | ||
|
|
8a8759b0d4 | ||
|
|
f259072136 | ||
|
|
3fd74292ef | ||
|
|
f37c4293df | ||
|
|
b9248553be | ||
|
|
475d434e98 | ||
|
|
05d568d1c1 | ||
|
|
28b63bac38 | ||
|
|
fc64719bb8 | ||
|
|
323a74eef3 | ||
|
|
bd4da4f365 | ||
|
|
ae7f7e9bd6 | ||
|
|
b88e63d133 | ||
|
|
a85255df5a | ||
|
|
15c02e9e91 | ||
|
|
ec35392cd1 | ||
|
|
b14b977bca | ||
|
|
54f55d44dc | ||
|
|
568dc1625e | ||
|
|
259b239e16 | ||
|
|
3033387ffc | ||
|
|
d500c29493 | ||
|
|
799d8003eb | ||
|
|
5d34af6ac5 | ||
|
|
dca5b9f197 | ||
|
|
75e075ef8e | ||
|
|
9895976128 | ||
|
|
133367ddb5 | ||
|
|
57964282ee | ||
|
|
51b07fc0c3 | ||
|
|
d8f33641a5 | ||
|
|
a7c7234589 | ||
|
|
f22f1971cb | ||
|
|
28a5806492 | ||
|
|
df98167706 | ||
|
|
06cc40055a | ||
|
|
66b47c08e6 | ||
|
|
40cb8db1fc | ||
|
|
2454a03c2b | ||
|
|
0a59a06121 | ||
|
|
a7191e8efb | ||
|
|
e1f4d791ae | ||
|
|
806fcb6ed2 | ||
|
|
467b0fa988 | ||
|
|
a420d5b203 | ||
|
|
5be3aec7d4 | ||
|
|
5e25a4a00e | ||
|
|
810cf9ab2c | ||
|
|
2a11de7571 | ||
|
|
12543be913 | ||
|
|
cfbf08dbf5 | ||
|
|
3649573ba1 | ||
|
|
0fdc02d5e9 | ||
|
|
0ffe8ef102 | ||
|
|
ce995812d4 | ||
|
|
cd22e1779d | ||
|
|
fbb50d8fb6 | ||
|
|
27172c2a19 | ||
|
|
f453dff438 | ||
|
|
a437a40eee | ||
|
|
70a173a16d | ||
|
|
7724fc74e5 | ||
|
|
6023c08554 | ||
|
|
2de56065af | ||
|
|
f5cf1ca08c | ||
|
|
37a1e67579 | ||
|
|
a372cbbf52 | ||
|
|
a67cf44453 | ||
|
|
a4bead1bde | ||
|
|
0d15024bd2 | ||
|
|
6509d726b3 | ||
|
|
a132f0f41d | ||
|
|
7c2ea3dcfd | ||
|
|
706c6d46da | ||
|
|
04915cc65a | ||
|
|
e2fb8827a7 | ||
|
|
05fda903c6 | ||
|
|
91e1eff763 | ||
|
|
2c4ffcfa7c | ||
|
|
34d63f3557 | ||
|
|
a5613ebb10 | ||
|
|
1a2b45eebd | ||
|
|
65791a58e9 | ||
|
|
6e0f22323b | ||
|
|
4c8c35a25a | ||
|
|
a149f06f58 | ||
|
|
42f97f63d5 | ||
|
|
227652de2e | ||
|
|
dd205e849c | ||
|
|
05d994889d | ||
|
|
4b381049c3 | ||
|
|
33f3479569 | ||
|
|
6b940ed675 | ||
|
|
247be0c11f | ||
|
|
14b0e8875d | ||
|
|
bd51741b01 | ||
|
|
afd60b8a16 | ||
|
|
6e0659af4c | ||
|
|
6de1fa0878 | ||
|
|
61e00f1620 | ||
|
|
c8c75b38c3 | ||
|
|
2bdd0a0e8d | ||
|
|
4131ad5016 | ||
|
|
4f74b3fafe | ||
|
|
177a487ead | ||
|
|
99a4966786 | ||
|
|
db9279a6e5 | ||
|
|
3036f43b75 | ||
|
|
f4560bd7d8 | ||
|
|
97b9ec5cc7 | ||
|
|
cdab3d0eff | ||
|
|
de991041c4 | ||
|
|
776c16cd49 | ||
|
|
d9f8746438 | ||
|
|
20d1c4d242 | ||
|
|
6edac37031 | ||
|
|
cea5a18a7a | ||
|
|
bf615303f1 | ||
|
|
063585f563 | ||
|
|
01304e807a | ||
|
|
d275b3fd08 | ||
|
|
66364354da | ||
|
|
48203aedf9 | ||
|
|
a6ca893ec5 | ||
|
|
d55f1163a2 | ||
|
|
80530012b8 | ||
|
|
d98a6adfb3 | ||
|
|
ae3a35816e | ||
|
|
8ca4be5ac0 | ||
|
|
1c814946d5 | ||
|
|
1db59fc052 | ||
|
|
273a69258a | ||
|
|
3b0dc2195d | ||
|
|
bbb2539ace | ||
|
|
03cfe9bf62 | ||
|
|
e29a4d79ba | ||
|
|
4243bf9ba8 | ||
|
|
7ae9627a79 | ||
|
|
097a1cc799 | ||
|
|
625511d28b | ||
|
|
b6a58ee845 | ||
|
|
a349aed94c | ||
|
|
82b96d1a14 | ||
|
|
b85ddd1a53 | ||
|
|
732f88ff51 | ||
|
|
c08139b812 | ||
|
|
a6a6e73b04 | ||
|
|
9971201004 | ||
|
|
e99b0f8052 | ||
|
|
7a59b2b25d | ||
|
|
521e21072e | ||
|
|
f34845fd7d | ||
|
|
29ccadbd43 | ||
|
|
ffb4b395d0 | ||
|
|
19eba9526a | ||
|
|
09ed76904b | ||
|
|
22932241bd | ||
|
|
afc95d99cb | ||
|
|
5fde3c53b8 | ||
|
|
1135852f25 | ||
|
|
e892f0cb45 | ||
|
|
de56374c4d | ||
|
|
9d92b8cc6d | ||
|
|
d9b3ba0ff2 | ||
|
|
4be6b11c1e | ||
|
|
6794da0738 | ||
|
|
5527170fab | ||
|
|
e732eed98a | ||
|
|
5244642dee | ||
|
|
b329a44fef | ||
|
|
c93632421d | ||
|
|
97e0fd9f43 | ||
|
|
ea536c5537 | ||
|
|
970c906c97 | ||
|
|
fe738da633 | ||
|
|
5e3da71ce4 | ||
|
|
a0cccb299e | ||
|
|
e56974f4b2 | ||
|
|
fd28da2a3b | ||
|
|
257b35e4ae | ||
|
|
4398563b85 | ||
|
|
e23ace1bb0 | ||
|
|
2ef333c66d | ||
|
|
c6ee44010c | ||
|
|
71d770e679 | ||
|
|
cf17b93201 | ||
|
|
08f284ce29 | ||
|
|
4336aeb0ad | ||
|
|
5fd3a45c64 | ||
|
|
77f8bfcf25 | ||
|
|
509a97e532 | ||
|
|
604683efd5 | ||
|
|
c731515b94 | ||
|
|
8d85158f91 | ||
|
|
0736f372dc | ||
|
|
a37210b22e | ||
|
|
ccf81ff7b9 | ||
|
|
a4c1a65f10 | ||
|
|
6bdea219bf | ||
|
|
02a82e1e59 | ||
|
|
d238b0b9f2 | ||
|
|
40026fbf53 | ||
|
|
4193818259 | ||
|
|
78840157ef | ||
|
|
f3d69a29b9 | ||
|
|
1ae6709b26 | ||
|
|
8cc44b3958 | ||
|
|
70e894c268 | ||
|
|
9ff3ab1a2b | ||
|
|
8167f6f232 | ||
|
|
de844e9b23 | ||
|
|
7214b71c29 | ||
|
|
be5df46803 | ||
|
|
25bd61558b | ||
|
|
41961de619 | ||
|
|
1ca3f6ef8a | ||
|
|
f97c94832a | ||
|
|
4ae7786362 | ||
|
|
d3d553ad5a | ||
|
|
4faab006e3 | ||
|
|
80621f598e | ||
|
|
eaf689fe2f | ||
|
|
211e3304f3 | ||
|
|
d4c3b70f15 | ||
|
|
f874941e4d | ||
|
|
437b2ab30a | ||
|
|
dcc2a5afe0 | ||
|
|
078e952f30 | ||
|
|
2d2fe4983c | ||
|
|
29ddc04f7b | ||
|
|
aac2da70cf | ||
|
|
f5cbe0c4fe | ||
|
|
ab28d0a9b3 | ||
|
|
6aaf4ed9d9 | ||
|
|
1b81a1f4a6 | ||
|
|
e838fe30de | ||
|
|
6d9779a497 | ||
|
|
130ece3d2e | ||
|
|
d63a794848 | ||
|
|
ba220abbbf | ||
|
|
3825575f70 | ||
|
|
8d69961130 | ||
|
|
740357d120 | ||
|
|
5638f0f24b | ||
|
|
c8a30e793a | ||
|
|
2264281e4d | ||
|
|
ce38d4ff6b | ||
|
|
74773ade62 | ||
|
|
3faf777db1 | ||
|
|
09e880a90d | ||
|
|
d5785e87e8 | ||
|
|
d4a43bf051 | ||
|
|
76c0432f96 | ||
|
|
4434796aab | ||
|
|
0faa24d5d3 | ||
|
|
6c3b87c9b9 | ||
|
|
2fb31f5944 | ||
|
|
8be42e78de | ||
|
|
6d242ee6c3 | ||
|
|
6c910d2b5f | ||
|
|
0caa56e618 | ||
|
|
e5dc843d6a | ||
|
|
3efd1ee325 | ||
|
|
56a1367322 | ||
|
|
544aba53dc | ||
|
|
d68bbbf9a1 | ||
|
|
489737274a | ||
|
|
4c1fabffc3 | ||
|
|
6a52d03329 | ||
|
|
17f00e6fea | ||
|
|
c67bc33c23 | ||
|
|
bb80dcb4e4 | ||
|
|
8fff3e5389 | ||
|
|
233109d23e | ||
|
|
a6aa78e3b6 | ||
|
|
7536322e53 | ||
|
|
b690e3d149 | ||
|
|
aabf270144 | ||
|
|
61a4f15e53 | ||
|
|
de212322e2 | ||
|
|
cda18fb957 | ||
|
|
011ba5056a | ||
|
|
44c871270b | ||
|
|
9f6b0e1018 | ||
|
|
2d9293732b | ||
|
|
d4a131ad31 | ||
|
|
fa2316fb28 | ||
|
|
6e5d1fb613 | ||
|
|
e45f0f0299 | ||
|
|
62125d0284 | ||
|
|
d5ffede5ea | ||
|
|
212bb33142 | ||
|
|
1d8c80c062 | ||
|
|
e8e11257d2 | ||
|
|
b4236b9a95 | ||
|
|
d2b1404907 | ||
|
|
f11aab6c19 | ||
|
|
1f133dbcd0 | ||
|
|
879576b613 | ||
|
|
7a105a1538 | ||
|
|
7408d7bc95 | ||
|
|
1d9820a7c9 | ||
|
|
c536f7d342 | ||
|
|
6d9dfb5deb | ||
|
|
a3d469a101 | ||
|
|
97ecb0a5d6 | ||
|
|
6f58e9e7bb | ||
|
|
0de19a09ad | ||
|
|
17a56189dc | ||
|
|
520f9801be | ||
|
|
03a6b0311f | ||
|
|
1a922d41df | ||
|
|
050f8f3c81 | ||
|
|
5fc32c7b85 | ||
|
|
964fa69fb4 | ||
|
|
f1a33db800 | ||
|
|
2e3b56c52f | ||
|
|
8f137cf49d | ||
|
|
de170ebdd2 | ||
|
|
e5ae1db49c | ||
|
|
63035196af | ||
|
|
e08d803a33 | ||
|
|
df1a939798 | ||
|
|
848f0db3d1 | ||
|
|
9e2d47eab5 | ||
|
|
88fa103acb | ||
|
|
9849bc055c | ||
|
|
1cf75a7169 | ||
|
|
e297dff0d4 | ||
|
|
aafd7ed21f | ||
|
|
ab2c31aa8a | ||
|
|
47111819e7 | ||
|
|
ddd3f7c196 | ||
|
|
1738f92ce2 | ||
|
|
074695b50c | ||
|
|
95ac136720 | ||
|
|
ecced27853 | ||
|
|
e86aa6b541 | ||
|
|
91f74b44a0 | ||
|
|
35d8e28b8c | ||
|
|
26a6af4ab2 | ||
|
|
20785926e2 | ||
|
|
66d0ee8156 | ||
|
|
6138b060d7 | ||
|
|
308d661d7e | ||
|
|
4e106c2355 | ||
|
|
456160fa29 | ||
|
|
b553ac58d3 | ||
|
|
2c26f43035 | ||
|
|
1b49844012 | ||
|
|
75d45e8907 | ||
|
|
b78bd5d6fd | ||
|
|
5d53344b75 | ||
|
|
7cd58bd534 | ||
|
|
9826bf67c2 | ||
|
|
350fe124d0 | ||
|
|
20aac83e5e | ||
|
|
9d1c822ba1 | ||
|
|
b8d46e2863 | ||
|
|
cd2f2d42a3 | ||
|
|
4bfab89292 | ||
|
|
1d3e8aedcd | ||
|
|
75c41644c6 | ||
|
|
3e221f3b6a | ||
|
|
535abdc481 | ||
|
|
3684126250 | ||
|
|
d3fc34c824 | ||
|
|
8769b5d1b5 | ||
|
|
59bb132dd7 | ||
|
|
8a53fa0aab | ||
|
|
71f42e405d | ||
|
|
64b89dd226 | ||
|
|
27d6b855b8 | ||
|
|
20c4f0dc10 | ||
|
|
7c166d85e1 | ||
|
|
093080894b | ||
|
|
9a2243da9f | ||
|
|
0f5aa9f8d9 | ||
|
|
673f7a82d1 | ||
|
|
87251878a5 | ||
|
|
3a14f363d9 | ||
|
|
84921f04cd | ||
|
|
b16475b8bd | ||
|
|
848e26eec6 | ||
|
|
62e86f9507 | ||
|
|
5b306f365e | ||
|
|
ce8950da43 | ||
|
|
0e1c7cd8ed | ||
|
|
fba6dd750d | ||
|
|
e73b6bc39e | ||
|
|
fa3cbbee67 | ||
|
|
029e52eb5f | ||
|
|
13914507e8 | ||
|
|
533f0213e3 | ||
|
|
95d6dac055 | ||
|
|
2001d7f02a | ||
|
|
bae5b70023 | ||
|
|
8341d343d3 | ||
|
|
278a56e580 | ||
|
|
6ab44ccf0c | ||
|
|
918898d6fe | ||
|
|
566e5dea57 | ||
|
|
5d1bfa18b1 | ||
|
|
800540ce7f | ||
|
|
74ee74365e | ||
|
|
75f6235111 | ||
|
|
fe415cbd77 | ||
|
|
dc69c0d3e5 | ||
|
|
40b3f39249 | ||
|
|
a9acc55735 | ||
|
|
9c07612274 | ||
|
|
3e87d88825 | ||
|
|
e280c0c5ca | ||
|
|
64f750a097 | ||
|
|
48bc18017e | ||
|
|
035fa3be3f | ||
|
|
f62723c274 | ||
|
|
aca59019c1 | ||
|
|
2186ea9a90 | ||
|
|
928f6e7009 | ||
|
|
64ed5fca3b | ||
|
|
4c7fc79843 | ||
|
|
153d77359f | ||
|
|
c8a2fb92d2 | ||
|
|
dcc252b371 | ||
|
|
846aadd7c6 | ||
|
|
3c604db234 | ||
|
|
cb3efb1c1d | ||
|
|
1423db35eb | ||
|
|
cb2a7a88fc | ||
|
|
e530111283 | ||
|
|
332c4a1f98 | ||
|
|
8541b95d8b | ||
|
|
e4584e3f04 | ||
|
|
168ec9092a | ||
|
|
914b6c757d | ||
|
|
48d10d701e | ||
|
|
6479e94c4f | ||
|
|
fb3a9f1f29 | ||
|
|
e9d7df8862 | ||
|
|
123de56f38 | ||
|
|
8627132d6a | ||
|
|
3ef9c4c626 | ||
|
|
aec03294d6 | ||
|
|
2e6e1f4060 | ||
|
|
07f1103784 | ||
|
|
ffc8f133c9 | ||
|
|
507f2a4c7c | ||
|
|
1c9e9b9367 | ||
|
|
4d81775f8d | ||
|
|
cf042931b6 | ||
|
|
f6a931599a | ||
|
|
1b17049e94 | ||
|
|
81c039d645 | ||
|
|
e245edc843 | ||
|
|
8494ed8ac3 | ||
|
|
371f14d2e4 | ||
|
|
1109fbfcc3 | ||
|
|
ff88f242fa | ||
|
|
bd3dd69ae1 | ||
|
|
06780c2805 | ||
|
|
e44e227af6 | ||
|
|
4ca06a9218 | ||
|
|
9e29761581 | ||
|
|
110c389d85 | ||
|
|
d856ed89ff | ||
|
|
fa747515e9 | ||
|
|
b15c9d7655 | ||
|
|
15c3ee4af2 | ||
|
|
748248deab | ||
|
|
70671ac672 | ||
|
|
f70f670ed3 | ||
|
|
4b685716d5 | ||
|
|
252c812125 | ||
|
|
69a437a1a8 | ||
|
|
b8b36b6248 | ||
|
|
28e24cb9d6 | ||
|
|
2a0609fdb8 | ||
|
|
b3233cab71 | ||
|
|
405a3eda60 | ||
|
|
49acfc496b | ||
|
|
a1932fa3dc | ||
|
|
87a8706299 | ||
|
|
92ec46147b | ||
|
|
2bda7566b4 | ||
|
|
43488cb57b | ||
|
|
137fbb638b | ||
|
|
76ed8d58fa | ||
|
|
8204139df8 | ||
|
|
7926684930 | ||
|
|
73946a4ede | ||
|
|
aa88115d45 | ||
|
|
496c08b075 | ||
|
|
2388d1782f | ||
|
|
e0e1c59d41 | ||
|
|
f2cb6ce972 | ||
|
|
ed3535e9c9 | ||
|
|
62f1830197 | ||
|
|
2fb62fe8be | ||
|
|
3243cbf039 | ||
|
|
eb171ecb21 | ||
|
|
43b8941f7a | ||
|
|
0b16839d7f | ||
|
|
164ba46259 | ||
|
|
b174f7a023 | ||
|
|
c9ec9f14de | ||
|
|
597c071c3d | ||
|
|
91d05434ca | ||
|
|
5b0aba99bb | ||
|
|
87c7d1a5f4 | ||
|
|
d841d69bd4 | ||
|
|
512bf1d013 | ||
|
|
adbd58a949 | ||
|
|
a0959d2730 | ||
|
|
f455722bed | ||
|
|
735d23c9eb | ||
|
|
0fd6a23c78 | ||
|
|
fd73d8d67e | ||
|
|
347366e8d7 | ||
|
|
eb8d4abcbe | ||
|
|
86acccebb0 | ||
|
|
8f184f76fa | ||
|
|
1daabfa3e0 | ||
|
|
90b49106f0 | ||
|
|
1a5638426c | ||
|
|
8d4da009af | ||
|
|
89571d57bd | ||
|
|
5d4e06074a | ||
|
|
98522cf6e4 | ||
|
|
7cdb751606 | ||
|
|
4ffc90f963 | ||
|
|
8da6572074 | ||
|
|
ee64aad670 | ||
|
|
92cee2321f | ||
|
|
7f969b1252 | ||
|
|
a02e2e1fa1 | ||
|
|
d690e1b37a | ||
|
|
3300c2302c | ||
|
|
685470cf23 | ||
|
|
c545a959ac | ||
|
|
a7832ef40f | ||
|
|
3e941999d5 | ||
|
|
bebdfc259c | ||
|
|
d31a920eb7 | ||
|
|
ec87821bcd | ||
|
|
21df0e32c4 | ||
|
|
afaa235c75 | ||
|
|
8d97e7136f | ||
|
|
89ac422929 | ||
|
|
d8d55f032e | ||
|
|
c83d694c7a | ||
|
|
ea55650d45 | ||
|
|
ec1aee60a1 | ||
|
|
3a7a0918d3 | ||
|
|
8bf90714da | ||
|
|
d8b59925b0 | ||
|
|
d37fd6bea0 | ||
|
|
0f6263d82e | ||
|
|
e7c70945e6 | ||
|
|
095a936891 | ||
|
|
c49ce91fac | ||
|
|
dd52727055 | ||
|
|
b48068c63c | ||
|
|
c39d04e8a5 | ||
|
|
0eff8e4bc8 | ||
|
|
178551a467 | ||
|
|
720739e50a | ||
|
|
46d1537d8f | ||
|
|
63b2bb55a1 | ||
|
|
06c210f5c8 | ||
|
|
a545c42047 | ||
|
|
da4b4de292 | ||
|
|
fd936e5bf3 | ||
|
|
dc11340faf | ||
|
|
06d921d855 | ||
|
|
91b602341b | ||
|
|
ad853ca342 | ||
|
|
14fa3481b0 | ||
|
|
2b6fee0455 | ||
|
|
a48e6f82d1 | ||
|
|
1020904fe0 | ||
|
|
6aaced0c04 | ||
|
|
3646c0d0a3 | ||
|
|
44c8574f1b | ||
|
|
ea1ff65db7 | ||
|
|
85b7147927 | ||
|
|
6b54c342aa | ||
|
|
60720b04db | ||
|
|
323a593159 | ||
|
|
baef94f14a | ||
|
|
59b3cfee66 | ||
|
|
cbc85ca98f | ||
|
|
425c8cd1d3 | ||
|
|
1d7949ded6 | ||
|
|
2507170816 | ||
|
|
2c83425b42 | ||
|
|
1da281de8a | ||
|
|
98baab4d03 | ||
|
|
441069aa76 | ||
|
|
8c5bf55b51 | ||
|
|
b6d6955b39 | ||
|
|
0d68b1a73e | ||
|
|
15b7c241ff | ||
|
|
de9c62617b | ||
|
|
a0e05115a3 | ||
|
|
aaf58272f1 | ||
|
|
5ebaf5663d | ||
|
|
7dc3e6a08b | ||
|
|
5a5f817e59 | ||
|
|
b650320a34 | ||
|
|
424fd8e4d3 | ||
|
|
cfbf80fdfe | ||
|
|
9b8ced2803 | ||
|
|
630a1d42b8 | ||
|
|
b69ff6ca8a | ||
|
|
1a23b6178b | ||
|
|
de536d7305 | ||
|
|
6dcd33bbc5 | ||
|
|
c6df78eb81 | ||
|
|
d1babe27ec | ||
|
|
7596f62ca5 | ||
|
|
139e57f1eb | ||
|
|
e87d85b088 | ||
|
|
91b79b703c | ||
|
|
bdefa4a967 | ||
|
|
b82efb6e3c | ||
|
|
a50e109043 | ||
|
|
8b5db12e1d | ||
|
|
f1f8163bb7 | ||
|
|
c0fac90b52 | ||
|
|
15717ddca0 | ||
|
|
a7ce9aaca5 | ||
|
|
d8df8e1033 | ||
|
|
440a35e491 | ||
|
|
dbbc6ed0bb | ||
|
|
86fbb383fd | ||
|
|
1c07a74bef | ||
|
|
2df93c2ceb | ||
|
|
6ca40afac0 | ||
|
|
36a8f044ae | ||
|
|
276e5453f1 | ||
|
|
0f81986684 | ||
|
|
bc54e536af | ||
|
|
acb57b3e9c | ||
|
|
de0e30de57 | ||
|
|
b8b942cdae | ||
|
|
0146a077e8 | ||
|
|
a0fcf515cd | ||
|
|
1719ad81a4 | ||
|
|
51a9a7b75d | ||
|
|
e91b0500c5 | ||
|
|
2a17361a90 | ||
|
|
1eca3a8603 | ||
|
|
86f37863a7 | ||
|
|
643cd87706 | ||
|
|
98c85441f8 | ||
|
|
1345f996da | ||
|
|
ff48f08241 | ||
|
|
b5f438aa0c | ||
|
|
a2266adb3f | ||
|
|
c518f373df | ||
|
|
f4636fdca2 | ||
|
|
4108d7827a | ||
|
|
5ccd40f530 | ||
|
|
8798340d48 | ||
|
|
e8d2959350 | ||
|
|
f1d838de6b | ||
|
|
ec92a4cd34 | ||
|
|
b3b9788d37 | ||
|
|
67739e8e39 | ||
|
|
be3ed7d380 | ||
|
|
9b4d58cfe8 | ||
|
|
fb6a8d999c | ||
|
|
b2f6a06a1f | ||
|
|
f7da32702f | ||
|
|
219d2246fa | ||
|
|
979b5eb89c | ||
|
|
a58811b1bf | ||
|
|
be0e18d4b0 | ||
|
|
fea6f0ce81 | ||
|
|
5859ac7a58 | ||
|
|
566833da6e | ||
|
|
d174602cc0 | ||
|
|
46bc04f69f | ||
|
|
bb8fd5500d | ||
|
|
d0df105dda | ||
|
|
a061fb5421 | ||
|
|
fd17e5a4c0 | ||
|
|
cd8bc7a7a1 | ||
|
|
e22a586a41 | ||
|
|
5017c5a5f1 | ||
|
|
03cbab5267 | ||
|
|
2c22837d9f | ||
|
|
4d64df37f5 | ||
|
|
b224f1d78d | ||
|
|
52e018989d | ||
|
|
eb14193b23 | ||
|
|
001c2c88cd | ||
|
|
98ab83976e | ||
|
|
9cd5bf2992 | ||
|
|
e876712af5 | ||
|
|
9fc8d66601 | ||
|
|
12d86493bf | ||
|
|
c97cba69b9 | ||
|
|
63ed7a734b | ||
|
|
0e338ef453 | ||
|
|
d914b38f9f | ||
|
|
3ee00a4824 | ||
|
|
5198c69f6b | ||
|
|
40704c4549 | ||
|
|
83df59e5f1 | ||
|
|
246602e275 | ||
|
|
d3449609da | ||
|
|
a872058704 | ||
|
|
3033376028 | ||
|
|
0bda636113 | ||
|
|
28092d6862 | ||
|
|
cecafb726c | ||
|
|
8418c21096 | ||
|
|
c4ace1972a | ||
|
|
862df1d2df | ||
|
|
487c1a58d6 | ||
|
|
c06c3d48d2 | ||
|
|
7d3ad5a639 | ||
|
|
03b2d02742 | ||
|
|
103ce44ba9 | ||
|
|
22219af133 | ||
|
|
7d7377ab15 | ||
|
|
af8fc53704 | ||
|
|
e80a5d82a4 | ||
|
|
2b73e745da | ||
|
|
e61a1080f7 | ||
|
|
464f336790 | ||
|
|
1dc6e085cc | ||
|
|
8594ff0c23 | ||
|
|
e58963410e | ||
|
|
1e0d118930 | ||
|
|
723e6dd13f | ||
|
|
e941af0905 | ||
|
|
24c8e29691 | ||
|
|
8911bdf966 | ||
|
|
0834759e1a | ||
|
|
bb8c0d264e | ||
|
|
c7da8c5fd3 | ||
|
|
30789ff6e2 | ||
|
|
e6ece10716 | ||
|
|
ea7a255006 | ||
|
|
98df1f5312 | ||
|
|
2bf08787d8 | ||
|
|
b8f7e4211c | ||
|
|
b581d0c44f | ||
|
|
0b613812f7 | ||
|
|
4d066a762f | ||
|
|
8a30a65818 | ||
|
|
01703aff43 | ||
|
|
9d62931af9 | ||
|
|
e53d8fd4fa | ||
|
|
0dbde32ed4 | ||
|
|
d506fdb68c | ||
|
|
9c10186158 | ||
|
|
3eed51dbac | ||
|
|
1c1855f0c3 | ||
|
|
e0a277fa45 | ||
|
|
3d6c7171d2 | ||
|
|
0fee90e4df | ||
|
|
0cced44491 | ||
|
|
f21f758591 | ||
|
|
51e9527d53 | ||
|
|
268cd131dc | ||
|
|
26997ad5c3 | ||
|
|
ce1786f128 | ||
|
|
888057f64a | ||
|
|
3c6c4d9601 | ||
|
|
5e8bcf8faf | ||
|
|
0d6f2f93bf | ||
|
|
937b00cfcc | ||
|
|
0d4432720c | ||
|
|
0ec49643ee | ||
|
|
0d80ea4f4c | ||
|
|
d2b26124e4 | ||
|
|
343116ea24 | ||
|
|
10dc699b21 | ||
|
|
015c5f07bc | ||
|
|
a0b6a3bed6 | ||
|
|
9de5950f4d | ||
|
|
26488b793a | ||
|
|
5a6c2dfcd5 | ||
|
|
7e891ecb79 | ||
|
|
12840f2428 | ||
|
|
0b160a6741 | ||
|
|
dff34a8ae2 | ||
|
|
8f780ad89e | ||
|
|
4624ef393d | ||
|
|
2ce8a890e2 | ||
|
|
90e0e9000e | ||
|
|
a6bb1c156d | ||
|
|
31db8161b5 | ||
|
|
c0d1448080 | ||
|
|
4e4f4e60b6 | ||
|
|
dcc83271af | ||
|
|
a38458c9be | ||
|
|
7934f65ae8 | ||
|
|
aea0cec061 | ||
|
|
7ac56b1a0c | ||
|
|
f5ed0b700f | ||
|
|
1ff3b83766 | ||
|
|
40eca1727e | ||
|
|
9505e68ef7 | ||
|
|
5847806a16 | ||
|
|
e427ea44aa | ||
|
|
8df8bf33a4 | ||
|
|
29d2b942e8 | ||
|
|
992cdb992a | ||
|
|
db0dc71319 | ||
|
|
2c871d1339 | ||
|
|
9673922ca1 | ||
|
|
d311870b61 | ||
|
|
8dd1ff593a | ||
|
|
0f64a769c2 | ||
|
|
a8d150db5c | ||
|
|
d7e6367ff3 | ||
|
|
7b84d8c3d5 | ||
|
|
e025df0824 | ||
|
|
c3392d5386 | ||
|
|
2c09e6a7c0 | ||
|
|
2a0740e135 | ||
|
|
ac65357c8a | ||
|
|
07c255f7f8 | ||
|
|
198b3c9f1b | ||
|
|
f5c567eb90 | ||
|
|
8ca09f6384 | ||
|
|
386dfe42d0 | ||
|
|
7445f43b1b | ||
|
|
5b77e78662 | ||
|
|
4b0b8d4de2 | ||
|
|
b7de47b6fb | ||
|
|
3434907f52 | ||
|
|
099adb3853 | ||
|
|
dd9b1c047c | ||
|
|
6084bb15cf | ||
|
|
42b568952e | ||
|
|
d3bac307bb | ||
|
|
7e488b0fa3 | ||
|
|
7d7a6191ce | ||
|
|
5c644b31ca | ||
|
|
7b4398dfee | ||
|
|
663418e943 | ||
|
|
969bec248a | ||
|
|
8f487d7d4e | ||
|
|
89ebce79b2 | ||
|
|
34ce510fbd | ||
|
|
39f29399e7 | ||
|
|
8cd4cc7d0d | ||
|
|
608ab8f4ad | ||
|
|
e95643f198 | ||
|
|
3b1712345b | ||
|
|
6effe47bcc | ||
|
|
205080a210 | ||
|
|
bc998988c2 | ||
|
|
80b470cfd3 | ||
|
|
3df2b4b7f8 | ||
|
|
6141b60294 | ||
|
|
e1cfb0cab6 | ||
|
|
9b9782ce94 | ||
|
|
5d9eb25e38 | ||
|
|
f4a518ce6b | ||
|
|
3656e6ef54 | ||
|
|
a545d7d8b1 | ||
|
|
3127f7a31b | ||
|
|
f561de200d | ||
|
|
d85a063db8 | ||
|
|
d6bf935ceb | ||
|
|
0d6ff822e7 | ||
|
|
1c0cc8e91c | ||
|
|
fb38573b7e | ||
|
|
8a2f45ec23 | ||
|
|
fee2eb9251 | ||
|
|
24035c4ae8 | ||
|
|
aa8215df34 | ||
|
|
eb78e3d640 | ||
|
|
f8201cd2bc | ||
|
|
f37b3ecd26 | ||
|
|
e6dba59ae6 | ||
|
|
a7bc6b4f36 | ||
|
|
cacc5ac803 | ||
|
|
46dc0f144b | ||
|
|
3cc5275592 | ||
|
|
a3955a1ba8 | ||
|
|
89dfaf1d5d | ||
|
|
fc6246c5cb | ||
|
|
ce8a0cab2a | ||
|
|
b36b6c78a2 | ||
|
|
5232dc6485 | ||
|
|
b1d9fc6c9a | ||
|
|
80ded84f7a | ||
|
|
8394535725 | ||
|
|
adf780d25b | ||
|
|
749ae3deb3 | ||
|
|
2d7760e67c | ||
|
|
c915926e0f | ||
|
|
25203934e6 | ||
|
|
4b1b2878ea | ||
|
|
b11d5514b9 |
@@ -1,3 +1,3 @@
|
|||||||
# Windows has stack overflows when calling from Tauri, so we increase compiler size
|
# Windows has stack overflows when calling from Tauri, so we increase compiler size
|
||||||
[target.'cfg(windows)']
|
[target.'cfg(windows)']
|
||||||
rustflags = ["-C", "link-args=/STACK:16777220"]
|
rustflags = ["-C", "link-args=/STACK:16777220"]
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
# Editor configuration, see http://editorconfig.org
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
max_line_length = 100
|
max_line_length = 100
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
59
.github/ISSUE_TEMPLATE/1-app-bug.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
name: 🎮 Modrinth App bug
|
||||||
|
description: Report an issue in the Modrinth Launcher.
|
||||||
|
labels: [bug, app]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Please confirm the following.
|
||||||
|
options:
|
||||||
|
- label: I checked the [existing issues](https://github.com/modrinth/code/issues) for duplicate problems
|
||||||
|
required: true
|
||||||
|
- label: I have tried resolving the issue using the [support portal](https://support.modrinth.com)
|
||||||
|
required: true
|
||||||
|
- label: I have ensured my Modrinth App installation is up to date
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: What version of the Modrinth App are you using?
|
||||||
|
description: Find this in ⚙️ Settings (bottom right) -> About -> App version.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: oses
|
||||||
|
attributes:
|
||||||
|
label: What operating systems are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- Windows
|
||||||
|
- MacOS
|
||||||
|
- Linux
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug
|
||||||
|
description: A clear and concise description of what the bug is. Include screenshots if applicable.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Steps to reproduce the behavior.
|
||||||
|
placeholder: |
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '...'
|
||||||
|
3. Scroll down to '...'
|
||||||
|
4. See error
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context about the problem here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
52
.github/ISSUE_TEMPLATE/2-web-bug.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: 🌐 Website bug (modrinth.com)
|
||||||
|
description: Report an issue on the Modrinth website.
|
||||||
|
labels: [bug, frontend]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Please confirm the following.
|
||||||
|
options:
|
||||||
|
- label: I checked the [existing issues](https://github.com/modrinth/code/issues) for duplicate problems
|
||||||
|
required: true
|
||||||
|
- label: I have tried resolving the issue using the [support portal](https://support.modrinth.com)
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: browsers
|
||||||
|
attributes:
|
||||||
|
label: What browsers are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- Chrome (including Arc, Brave, Opera, Vivaldi)
|
||||||
|
- Microsoft Edge
|
||||||
|
- Firefox
|
||||||
|
- Safari
|
||||||
|
- Other (please specify)
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug
|
||||||
|
description: A clear and concise description of what the bug is. Include screenshots if applicable.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Steps to reproduce the behavior.
|
||||||
|
placeholder: |
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '...'
|
||||||
|
3. Scroll down to '...'
|
||||||
|
4. See error
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context about the problem here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
@@ -1,11 +1,19 @@
|
|||||||
name: Bug report
|
name: 🛠️ API issue (api.modrinth.com)
|
||||||
description: Create a report to help us improve Modrinth
|
description: Report an issue regarding the Modrinth API.
|
||||||
labels: [bug]
|
labels: [bug, api]
|
||||||
body:
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Please confirm the following.
|
||||||
|
options:
|
||||||
|
- label: I checked the [existing issues](https://github.com/modrinth/code/issues) for duplicate problems
|
||||||
|
required: true
|
||||||
|
- label: I have tried resolving the issue using the [support portal](https://support.modrinth.com)
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Describe the bug
|
label: Describe the bug
|
||||||
description: A clear and concise description of what the bug is.
|
description: A clear and concise description of what the bug is. Include screenshots if applicable.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@@ -25,13 +33,9 @@ body:
|
|||||||
description: A clear and concise description of what you expected to happen.
|
description: A clear and concise description of what you expected to happen.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: System information
|
|
||||||
description: Add any information about what OS you are on (like Windows or Mac), and what version of the app you are using.
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional context
|
label: Additional context
|
||||||
description: Add any other context about the problem here. This might include logs, screenshots, etc. The more the merrier!
|
description: Add any other context about the problem here.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
@@ -1,10 +1,28 @@
|
|||||||
name: Enhancement
|
name: 💡 Feature Request
|
||||||
description: Suggest an enhancement for an existing Modrinth feature
|
description: Suggest an idea
|
||||||
labels: [enhancement]
|
labels: [enhancement]
|
||||||
|
|
||||||
body:
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Please confirm the following.
|
||||||
|
options:
|
||||||
|
- label: I checked the [existing issues](https://github.com/modrinth/code/issues) for duplicate feature requests
|
||||||
|
required: true
|
||||||
|
- label: I have checked that this feature request is not on our [roadmap](https://roadmap.modrinth.com)
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: projects
|
||||||
|
attributes:
|
||||||
|
label: What parts of Modrinth is your feature request related too?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- App
|
||||||
|
- Website
|
||||||
|
- API
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Is your suggested enhancement related to a problem? Please describe.
|
label: Is your suggested feature related to a problem? Please describe.
|
||||||
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
@@ -25,4 +43,4 @@ body:
|
|||||||
label: Additional context
|
label: Additional context
|
||||||
description: Add any other context or screenshots about the suggested enhancement here.
|
description: Add any other context or screenshots about the suggested enhancement here.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
20
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,16 +1,14 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Discord
|
- name: 🫶 Support Portal
|
||||||
about: Ask questions on our Discord Server.
|
about: Get support using through our portal.
|
||||||
|
url: https://support.modrinth.com
|
||||||
|
- name: 💬 Chat
|
||||||
|
about: Join our Discord server to chat about Modrinth.
|
||||||
url: https://discord.modrinth.com
|
url: https://discord.modrinth.com
|
||||||
- name: Roadmap
|
- name: 🛣️ Roadmap
|
||||||
about: View our Roadmap. Please do not open issues for items on our roadmap.
|
about: View our Roadmap. Please do not open issues for items on our roadmap.
|
||||||
url: https://roadmap.modrinth.com
|
url: https://roadmap.modrinth.com
|
||||||
- name: Discussions (Feature requests)
|
- name: 📚 Documentation
|
||||||
about: |
|
about: Useful documentation about Modrinth's API
|
||||||
Please use Issues for reporting bugs and suggesting enhancements to existing features.
|
url: https://docs.modrinth.com
|
||||||
Suggestions for new features should be made as a Discussion.
|
|
||||||
url: https://github.com/orgs/modrinth/discussions/categories/feature-requests
|
|
||||||
- name: Documentation
|
|
||||||
about: Useful documentation about Modrinth, its API, and how you can contribute.
|
|
||||||
url: https://docs.modrinth.com
|
|
||||||
BIN
.github/assets/app_cover.png
vendored
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
.github/assets/monorepo_cover.png
vendored
Normal file
|
After Width: | Height: | Size: 417 KiB |
BIN
.github/assets/web_cover.png
vendored
Normal file
|
After Width: | Height: | Size: 41 KiB |
@@ -1,19 +1,28 @@
|
|||||||
name: 'Tauri GUI Build'
|
name: 'Modrinth App build'
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches:
|
||||||
pull_request:
|
- main
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
paths:
|
||||||
|
- 'apps/app/**'
|
||||||
|
- 'apps/app-frontend/**'
|
||||||
|
- 'packages/app-lib/**'
|
||||||
|
- 'packages/app-macros/**'
|
||||||
|
- 'packages/assets/**'
|
||||||
|
- 'packages/ui/**'
|
||||||
|
- 'packages/utils/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-tauri:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
platform: [macos-latest, windows-latest, ubuntu-20.04, ubuntu-22.04]
|
platform: [macos-latest, windows-latest, ubuntu-20.04]
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./theseus_gui
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -31,25 +40,31 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
- name: Rust cache
|
- name: Setup rust cache
|
||||||
uses: swatinem/rust-cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
workspaces: './src-tauri -> target'
|
path: target/**
|
||||||
|
key: ${{ runner.os }}-rust-target-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-rust-target-
|
||||||
|
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18.x
|
node-version: 20
|
||||||
|
|
||||||
- name: Install pnpm via corepack
|
- name: Install pnpm via corepack
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
corepack enable
|
corepack enable
|
||||||
corepack prepare --activate
|
corepack prepare --activate
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
id: pnpm-cache
|
id: pnpm-cache
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Setup pnpm cache
|
- name: Setup pnpm cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
@@ -83,7 +98,8 @@ jobs:
|
|||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
with:
|
with:
|
||||||
args: --target universal-apple-darwin
|
args: "--target universal-apple-darwin --config ./apps/app/tauri-release.conf.json"
|
||||||
|
working-directory: ./apps/app
|
||||||
|
|
||||||
- name: build app
|
- name: build app
|
||||||
uses: tauri-apps/tauri-action@v0
|
uses: tauri-apps/tauri-action@v0
|
||||||
@@ -93,6 +109,9 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
with:
|
||||||
|
args: "--config ./apps/app/tauri-release.conf.json"
|
||||||
|
working-directory: ./apps/app
|
||||||
|
|
||||||
- name: upload ${{ matrix.platform }}
|
- name: upload ${{ matrix.platform }}
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
70
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
merge_group:
|
||||||
|
types: [ checks_requested ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build, Test, and Lint
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Cache turbo build setup
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: .turbo
|
||||||
|
key: ${{ runner.os }}-turbo-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-turbo-
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libselinux1
|
||||||
|
|
||||||
|
- name: Setup Node.JS environment
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Install pnpm via corepack
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
corepack enable
|
||||||
|
corepack prepare --activate
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
id: pnpm-cache
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
name: Setup pnpm cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: pnpm lint
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: pnpm test
|
||||||
28
.github/workflows/frontend-pages.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Deploy frontend
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
wait:
|
||||||
|
if: github.repository_owner == 'modrinth'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
deployments: write
|
||||||
|
steps:
|
||||||
|
- name: Cloudflare Pages deployment
|
||||||
|
uses: WalshyDev/cf-pages-await@v1
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||||
|
accountId: '9ddae624c98677d68d93df6e524a6061'
|
||||||
|
project: 'frontend'
|
||||||
|
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
commitHash: ${{ steps.push-changes.outputs.commit-hash }}
|
||||||
|
- name: Purge cache
|
||||||
|
if: github.ref == 'refs/heads/prod'
|
||||||
|
run: |
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer ${{ secrets.CF_API_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data '{"hosts": ["modrinth.com", "www.modrinth.com"]}' \
|
||||||
|
https://api.cloudflare.com/client/v4/zones/e39df17b9c4ef44cbce2646346ee6d33/purge_cache
|
||||||
42
.github/workflows/gui-build.yml
vendored
@@ -1,42 +0,0 @@
|
|||||||
name: GUI Build + Lint
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
pull_request:
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./theseus_gui
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Use Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18.x
|
|
||||||
- name: Install pnpm via corepack
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
corepack enable
|
|
||||||
corepack prepare --activate
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
- name: Setup pnpm cache
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
- name: Run Lint
|
|
||||||
run: pnpm lint
|
|
||||||
- name: Build
|
|
||||||
run: pnpm build
|
|
||||||
152
.gitignore
vendored
@@ -1,115 +1,57 @@
|
|||||||
/target
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
node_modules/
|
|
||||||
.svelte-kit/
|
|
||||||
theseus_gui/build/
|
|
||||||
theseus_gui/generated/
|
|
||||||
WixTools
|
|
||||||
.direnv/
|
|
||||||
.DS_Store
|
|
||||||
.pnpm-debug.log
|
|
||||||
|
|
||||||
minecraft
|
# compiled output
|
||||||
config
|
dist
|
||||||
|
tmp
|
||||||
|
/out-tsc
|
||||||
|
|
||||||
[#]*[#]
|
# dependencies
|
||||||
|
node_modules
|
||||||
|
|
||||||
# TEMPORARY: ignore my test instance and metadata
|
# IDEs and editors
|
||||||
theseus_cli/foo
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
### Intellij+all ###
|
# IDE - VSCode
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
|
||||||
|
|
||||||
# User-specific stuff
|
|
||||||
.idea/**/workspace.xml
|
|
||||||
.idea/**/tasks.xml
|
|
||||||
.idea/**/usage.statistics.xml
|
|
||||||
.idea/**/dictionaries
|
|
||||||
.idea/**/shelf
|
|
||||||
|
|
||||||
# AWS User-specific
|
|
||||||
.idea/**/aws.xml
|
|
||||||
|
|
||||||
# Generated files
|
|
||||||
.idea/**/contentModel.xml
|
|
||||||
|
|
||||||
# Sensitive or high-churn files
|
|
||||||
.idea/**/dataSources/
|
|
||||||
.idea/**/dataSources.ids
|
|
||||||
.idea/**/dataSources.local.xml
|
|
||||||
.idea/**/sqlDataSources.xml
|
|
||||||
.idea/**/dynamic.xml
|
|
||||||
.idea/**/uiDesigner.xml
|
|
||||||
.idea/**/dbnavigator.xml
|
|
||||||
|
|
||||||
# Gradle
|
|
||||||
.idea/**/gradle.xml
|
|
||||||
.idea/**/libraries
|
|
||||||
|
|
||||||
# Gradle and Maven with auto-import
|
|
||||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
|
||||||
# since they will be recreated, and may cause churn. Uncomment if using
|
|
||||||
# auto-import.
|
|
||||||
# .idea/artifacts
|
|
||||||
# .idea/compiler.xml
|
|
||||||
# .idea/jarRepositories.xml
|
|
||||||
# .idea/modules.xml
|
|
||||||
# .idea/*.iml
|
|
||||||
# .idea/modules
|
|
||||||
# *.iml
|
|
||||||
# *.ipr
|
|
||||||
|
|
||||||
# CMake
|
|
||||||
cmake-build-*/
|
|
||||||
|
|
||||||
# Mongo Explorer plugin
|
|
||||||
.idea/**/mongoSettings.xml
|
|
||||||
|
|
||||||
# File-based project format
|
|
||||||
*.iws
|
|
||||||
|
|
||||||
# IntelliJ
|
|
||||||
out/
|
|
||||||
|
|
||||||
# mpeltonen/sbt-idea plugin
|
|
||||||
.idea_modules/
|
|
||||||
|
|
||||||
# JIRA plugin
|
|
||||||
atlassian-ide-plugin.xml
|
|
||||||
|
|
||||||
# Cursive Clojure plugin
|
|
||||||
.idea/replstate.xml
|
|
||||||
|
|
||||||
# SonarLint plugin
|
|
||||||
.idea/sonarlint/
|
|
||||||
|
|
||||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
||||||
com_crashlytics_export_strings.xml
|
|
||||||
crashlytics.properties
|
|
||||||
crashlytics-build.properties
|
|
||||||
fabric.properties
|
|
||||||
|
|
||||||
# Editor-based Rest Client
|
|
||||||
.idea/httpRequests
|
|
||||||
|
|
||||||
# Android studio 3.1+ serialized cache file
|
|
||||||
.idea/caches/build_file_checksums.ser
|
|
||||||
|
|
||||||
### Intellij+all Patch ###
|
|
||||||
# Ignore everything but code style settings and run configurations
|
|
||||||
# that are supposed to be shared within teams.
|
|
||||||
|
|
||||||
.idea/*
|
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
!.idea/codeStyles
|
# misc
|
||||||
!.idea/runConfigurations
|
/.sass-cache
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# System Files
|
||||||
**/*.rs.bk
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
# Nuxt dev/build outputs
|
||||||
*.pdb
|
.output
|
||||||
|
.data
|
||||||
|
.nuxt
|
||||||
|
.nitro
|
||||||
|
.cache
|
||||||
|
|
||||||
theseus.iml
|
# frontend generated files
|
||||||
|
apps/frontend/src/generated
|
||||||
|
|
||||||
|
.turbo
|
||||||
|
target
|
||||||
|
generated
|
||||||
|
.env
|
||||||
|
|
||||||
|
# app testing dir
|
||||||
|
app-playground-data/*
|
||||||
|
|||||||
2
.npmrc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
strict-peer-dependencies=false
|
||||||
|
auto-install-peers=true
|
||||||
3
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["esbenp.prettier-vscode", "Vue.volar", "rust-lang.rust-analyzer"]
|
||||||
|
}
|
||||||
13
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"prettier.endOfLine": "lf",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"typescriptreact"
|
||||||
|
],
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": "explicit"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
COPYING.md
@@ -1,13 +1,13 @@
|
|||||||
# Copying
|
# Copying Guidelines
|
||||||
|
|
||||||
The source code of the theseus repository is licensed under the GNU General Public License, Version 3 only, which is provided in the file [LICENSE](./LICENSE). However, some files listed below are licensed under a different license.
|
All packages in this repository are licensed under their respective licenses. For more information, refer to the LICENSE file in each package.
|
||||||
|
|
||||||
## Modrinth logo
|
For detailed information, consult each package's COPYING.md file, if available.
|
||||||
|
|
||||||
Any files depicting the Modrinth branding, including the wrench-in-labyrinth logo, the landing image, and variations thereof, are licensed as follows:
|
## Modrinth Branding
|
||||||
|
|
||||||
> All rights reserved. © 2020-2023 Rinth, Inc.
|
The use of Modrinth branding elements, including but not limited to the wrench-in-labyrinth logo, the landing image, and any variations thereof, is strictly prohibited without explicit written permission from Rinth, Inc. This includes trademarks, logos, or other branding elements.
|
||||||
|
|
||||||
This includes, but may not be limited to, the following files:
|
All rights reserved. © 2020-2024 Rinth, Inc.
|
||||||
|
|
||||||
- theseus_gui/src-tauri/icons/*
|
If you fork this repository, you must remove all Modrinth branding assets from your fork.
|
||||||
|
|||||||
2388
Cargo.lock
generated
24
Cargo.toml
@@ -1,24 +1,11 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = '2'
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"theseus",
|
'./packages/app-lib',
|
||||||
"theseus_playground",
|
'./apps/app-playground',
|
||||||
"theseus_gui/src-tauri",
|
'./apps/app'
|
||||||
"theseus_macros"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
opt-level = 0
|
|
||||||
debug = true
|
|
||||||
debug-assertions = true
|
|
||||||
overflow-checks = true
|
|
||||||
lto = false
|
|
||||||
panic = 'unwind'
|
|
||||||
incremental = true
|
|
||||||
codegen-units = 256
|
|
||||||
rpath = false
|
|
||||||
|
|
||||||
# Optimize for speed and reduce size on release builds
|
# Optimize for speed and reduce size on release builds
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort" # Strip expensive panic clean-up logic
|
panic = "abort" # Strip expensive panic clean-up logic
|
||||||
@@ -26,3 +13,6 @@ codegen-units = 1 # Compile crates one after another so the compiler can optimiz
|
|||||||
lto = true # Enables link to optimizations
|
lto = true # Enables link to optimizations
|
||||||
opt-level = "s" # Optimize for binary size
|
opt-level = "s" # Optimize for binary size
|
||||||
strip = true # Remove debug symbols
|
strip = true # Remove debug symbols
|
||||||
|
|
||||||
|
[profile.dev.package.sqlx-macros]
|
||||||
|
opt-level = 3
|
||||||
|
|||||||
60
README.md
@@ -1,45 +1,39 @@
|
|||||||
<img src="https://github.com/modrinth/theseus/assets/6166773/51d1ca87-05c0-445a-bd18-ddd1117f7f12" alt="modrinth app: theseus (desktop app)">
|
# 
|
||||||
|
|
||||||
# Modrinth App
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
<img src="https://cdn-raw.modrinth.com/app-landing/app-screenshot.webp" alt="Screenshot of the Modrinth App's home page" align="right" width="50%">
|
## Modrinth Monorepo
|
||||||
|
|
||||||
The Modrinth App, codenamed theseus, is a modern launcher for Minecraft: Java Edition with a clean look, easy-to-use interface, and deep integration into Modrinth services.
|
Welcome to the Modrinth Monorepo, the primary codebase for the Modrinth web interface and app. It contains  lines of code and has  contributors!
|
||||||
|
|
||||||
### Features
|
If you're not a developer and you've stumbled upon this repository, you can access the web interface on the [Modrinth website](https://modrinth.com) and download the latest release of the app [here](https://modrinth.com/app).
|
||||||
- One-click installation of modpacks
|
|
||||||
- Automatic management of Java versions
|
|
||||||
- Windows, Mac, and Linux[^1] support
|
|
||||||
- Import your instances from CurseForge, Prism[^2], ATLauncher, MultiMC[^2], or GDLauncher
|
|
||||||
- Supports offline play once you've authenticated with your Minecraft account at least once
|
|
||||||
- Fully open source under GPLv3[^3]!
|
|
||||||
|
|
||||||
[^1]: While Linux is supported, due to the wide range of distributions out there, your mileage may vary with how well the Modrinth App works on your system. We officially distribute `.deb` and `.AppImage` packages, but third party packages have been created for a number of other package platforms. Additionally, some have reported lag issues running on Linux, we believe this to be due to an upstream Tauri issue, which we hope improves with further development.
|
|
||||||
|
|
||||||
[^2]: Certain features of the OneSix format used by Prism and MultiMC are not yet supported, so some instances may not import correctly, primarily on older Minecraft versions or unsupported mod loaders.
|
|
||||||
|
|
||||||
[^3]: Modrinth's logos, branding, and other trademarks are not free for use, see the [licensing section](#license) for more information.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
You're welcome to help contribute to the Modrinth App if you'd like! Please review our [contribution guide](https://support.modrinth.com/en/articles/8802215-contributing-to-modrinth) before attempting to contribute or make a pull request though.
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
To get started, install [pnpm](https://pnpm.io/), [Rust](https://www.rust-lang.org/tools/install), and the [Tauri prerequisites](https://tauri.app/v1/guides/getting-started/prerequisites/#installing) for your system. Then, run the following commands:
|
|
||||||
|
|
||||||
```
|
This repository contains two primary packages. For detailed development information, please refer to their respective READMEs:
|
||||||
cd theseus_gui
|
|
||||||
pnpm install
|
|
||||||
pnpm run tauri dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Once the commands finish, you'll be viewing a Tauri window with Nuxt.js hot reloading.
|
- [Web Interface](apps/frontend/README.md)
|
||||||
|
- [Desktop App](apps/app/README.md)
|
||||||
|
|
||||||
You can use `pnpm run lint` to find any eslint problems, and `pnpm run fix` to try automatically fixing those problems.
|
## Contributing
|
||||||
|
|
||||||
|
We welcome contributions! Before submitting any contributions, please read our [contributing guidelines](https://support.modrinth.com/en/articles/8802215-contributing-to-modrinth).
|
||||||
|
|
||||||
|
If you plan to fork this repository for your own purposes, please review our [copying guidelines](COPYING.md).
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
If you discover a security vulnerability within our codebase, please follow our [responsible disclosure guidelines](https://modrinth.com/legal/security).
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
If you need help with the Modrinth web interface or app, please visit our [support page](https://support.modrinth.com). For general inquiries, you can also join our [Discord server](https://discord.modrinth.com).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
The source code of the theseus repository is licensed under the GNU General Public License, Version 3 only, which is provided in the file [LICENSE](https://github.com/modrinth/theseus/blob/master/LICENSE). However, some files are licensed under a different license.
|
|
||||||
|
|
||||||
Any files depicting the Modrinth branding, including the wrench-in-labyrinth logo, the landing image, and variations thereof, are licensed as follows:
|
All packages in this repository are licensed under their respective licenses. Refer to the LICENSE file in each package for more information.
|
||||||
> All rights reserved. © 2020-2024 Rinth, Inc.
|
|
||||||
|
|
||||||
Forking is permitted under the GPLv3, however do be aware that you must remove all Modrinth branding, including logos, brand colors, background images, or anything else that is related to trademarks or copyrights held by Rinth, Inc.
|
|
||||||
|
|||||||
4
apps/app-frontend/.eslintrc.cjs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: ['custom/vue'],
|
||||||
|
}
|
||||||
1
apps/app-frontend/.prettierignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
**/dist
|
||||||
13
apps/app-frontend/COPYING.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Copying
|
||||||
|
|
||||||
|
The source code of the theseus repository is licensed under the GNU General Public License, Version 3 only, which is provided in the file [LICENSE](./LICENSE). However, some files listed below are licensed under a different license.
|
||||||
|
|
||||||
|
## Modrinth logo
|
||||||
|
|
||||||
|
The use of Modrinth branding elements, including but not limited to the wrench-in-labyrinth logo, the landing image, and any variations thereof, is strictly prohibited without explicit written permission from Rinth, Inc. This includes trademarks, logos, or other branding elements.
|
||||||
|
|
||||||
|
> All rights reserved. © 2020-2023 Rinth, Inc.
|
||||||
|
|
||||||
|
This includes, but may not be limited to, the following files:
|
||||||
|
|
||||||
|
- theseus_gui/src-tauri/icons/\*
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" class="dark-mode">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Modrinth App</title>
|
<title>Modrinth App</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/src/assets/stylesheets/global.scss" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
44
apps/app-frontend/package.json
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name": "@modrinth/app-frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.8.3-1",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"lint": "eslint . && prettier --check .",
|
||||||
|
"fix": "eslint . --fix && prettier --write ."
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@modrinth/assets": "workspace:*",
|
||||||
|
"@modrinth/ui": "workspace:*",
|
||||||
|
"@modrinth/utils": "workspace:*",
|
||||||
|
"@tauri-apps/api": "^1.6.0",
|
||||||
|
"@vintl/vintl": "^4.4.1",
|
||||||
|
"dayjs": "^1.11.10",
|
||||||
|
"floating-vue": "^5.2.2",
|
||||||
|
"mixpanel-browser": "^2.49.0",
|
||||||
|
"ofetch": "^1.3.4",
|
||||||
|
"pinia": "^2.1.7",
|
||||||
|
"tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1",
|
||||||
|
"vite-svg-loader": "^5.1.0",
|
||||||
|
"vue": "^3.4.21",
|
||||||
|
"vue-multiselect": "3.0.0",
|
||||||
|
"vue-router": "4.3.0",
|
||||||
|
"vue-virtual-scroller": "v2.0.0-beta.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tauri-apps/cli": "^1.6.0",
|
||||||
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
|
"autoprefixer": "^10.4.19",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-config-custom": "workspace:*",
|
||||||
|
"postcss": "^8.4.39",
|
||||||
|
"prettier": "^3.2.5",
|
||||||
|
"sass": "^1.74.1",
|
||||||
|
"tailwindcss": "^3.4.4",
|
||||||
|
"tsconfig": "workspace:*",
|
||||||
|
"vite": "^5.2.8"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@9.4.0"
|
||||||
|
}
|
||||||
6
apps/app-frontend/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,18 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, onMounted } from 'vue'
|
||||||
import { RouterView, RouterLink, useRouter, useRoute } from 'vue-router'
|
import { RouterView, RouterLink, useRouter, useRoute } from 'vue-router'
|
||||||
import {
|
import { HomeIcon, SearchIcon, LibraryIcon, PlusIcon, SettingsIcon, XIcon } from '@modrinth/assets'
|
||||||
HomeIcon,
|
import { Button, Notifications } from '@modrinth/ui'
|
||||||
SearchIcon,
|
|
||||||
LibraryIcon,
|
|
||||||
PlusIcon,
|
|
||||||
SettingsIcon,
|
|
||||||
FileIcon,
|
|
||||||
Button,
|
|
||||||
Notifications,
|
|
||||||
XIcon,
|
|
||||||
Card,
|
|
||||||
} from 'omorphia'
|
|
||||||
import { useLoading, useTheming } from '@/store/state'
|
import { useLoading, useTheming } from '@/store/state'
|
||||||
import AccountsCard from '@/components/ui/AccountsCard.vue'
|
import AccountsCard from '@/components/ui/AccountsCard.vue'
|
||||||
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
||||||
@@ -23,11 +13,11 @@ import SplashScreen from '@/components/ui/SplashScreen.vue'
|
|||||||
import ErrorModal from '@/components/ui/ErrorModal.vue'
|
import ErrorModal from '@/components/ui/ErrorModal.vue'
|
||||||
import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
||||||
import { handleError, useNotifications } from '@/store/notifications.js'
|
import { handleError, useNotifications } from '@/store/notifications.js'
|
||||||
import { offline_listener, command_listener, warning_listener } from '@/helpers/events.js'
|
import { command_listener, warning_listener } from '@/helpers/events.js'
|
||||||
import { MinimizeIcon, MaximizeIcon, ChatIcon } from '@/assets/icons'
|
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
|
||||||
import { type } from '@tauri-apps/api/os'
|
import { type } from '@tauri-apps/api/os'
|
||||||
import { appWindow } from '@tauri-apps/api/window'
|
import { appWindow } from '@tauri-apps/api/window'
|
||||||
import { isDev, getOS, isOffline, showLauncherLogsFolder } from '@/helpers/utils.js'
|
import { isDev, getOS } from '@/helpers/utils.js'
|
||||||
import {
|
import {
|
||||||
mixpanel_track,
|
mixpanel_track,
|
||||||
mixpanel_init,
|
mixpanel_init,
|
||||||
@@ -38,131 +28,108 @@ import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
|||||||
import { getVersion } from '@tauri-apps/api/app'
|
import { getVersion } from '@tauri-apps/api/app'
|
||||||
import { window as TauriWindow } from '@tauri-apps/api'
|
import { window as TauriWindow } from '@tauri-apps/api'
|
||||||
import { TauriEvent } from '@tauri-apps/api/event'
|
import { TauriEvent } from '@tauri-apps/api/event'
|
||||||
import { await_sync, check_safe_loading_bars_complete } from './helpers/state'
|
|
||||||
import { confirm } from '@tauri-apps/api/dialog'
|
|
||||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||||
import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
|
||||||
import { install_from_file } from './helpers/pack'
|
import { install_from_file } from './helpers/pack'
|
||||||
import { useError } from '@/store/error.js'
|
import { useError } from '@/store/error.js'
|
||||||
|
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
|
||||||
|
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
|
||||||
|
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
|
||||||
|
import { useInstall } from '@/store/install.js'
|
||||||
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
import { get_opening_command, initialize_state } from '@/helpers/state'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
const urlModal = ref(null)
|
|
||||||
const isLoading = ref(true)
|
|
||||||
|
|
||||||
const offline = ref(false)
|
const urlModal = ref(null)
|
||||||
|
|
||||||
|
const offline = ref(!navigator.onLine)
|
||||||
|
window.addEventListener('offline', () => {
|
||||||
|
offline.value = true
|
||||||
|
})
|
||||||
|
window.addEventListener('online', () => {
|
||||||
|
offline.value = false
|
||||||
|
})
|
||||||
|
|
||||||
const showOnboarding = ref(false)
|
const showOnboarding = ref(false)
|
||||||
const nativeDecorations = ref(false)
|
const nativeDecorations = ref(false)
|
||||||
|
|
||||||
const onboardingVideo = ref()
|
|
||||||
|
|
||||||
const failureText = ref(null)
|
|
||||||
const os = ref('')
|
const os = ref('')
|
||||||
|
|
||||||
defineExpose({
|
const stateInitialized = ref(false)
|
||||||
initialize: async () => {
|
|
||||||
isLoading.value = false
|
|
||||||
const {
|
|
||||||
native_decorations,
|
|
||||||
theme,
|
|
||||||
opt_out_analytics,
|
|
||||||
collapsed_navigation,
|
|
||||||
advanced_rendering,
|
|
||||||
fully_onboarded,
|
|
||||||
} = await get()
|
|
||||||
// video should play if the user is not on linux, and has not onboarded
|
|
||||||
os.value = await getOS()
|
|
||||||
const dev = await isDev()
|
|
||||||
const version = await getVersion()
|
|
||||||
showOnboarding.value = !fully_onboarded
|
|
||||||
|
|
||||||
nativeDecorations.value = native_decorations
|
async function setupApp() {
|
||||||
if (os.value !== 'MacOS') appWindow.setDecorations(native_decorations)
|
stateInitialized.value = true
|
||||||
|
const {
|
||||||
|
native_decorations,
|
||||||
|
theme,
|
||||||
|
telemetry,
|
||||||
|
collapsed_navigation,
|
||||||
|
advanced_rendering,
|
||||||
|
onboarded,
|
||||||
|
default_page,
|
||||||
|
} = await get()
|
||||||
|
|
||||||
themeStore.setThemeState(theme)
|
if (default_page && default_page !== 'Home') {
|
||||||
themeStore.collapsedNavigation = collapsed_navigation
|
await router.push({ name: default_page })
|
||||||
themeStore.advancedRendering = advanced_rendering
|
}
|
||||||
|
|
||||||
mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
|
os.value = await getOS()
|
||||||
if (opt_out_analytics) {
|
const dev = await isDev()
|
||||||
mixpanel_opt_out_tracking()
|
const version = await getVersion()
|
||||||
}
|
showOnboarding.value = !onboarded
|
||||||
mixpanel_track('Launched', { version, dev, fully_onboarded })
|
|
||||||
|
|
||||||
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
nativeDecorations.value = native_decorations
|
||||||
|
if (os.value !== 'MacOS') await appWindow.setDecorations(native_decorations)
|
||||||
|
|
||||||
if ((await type()) === 'Darwin') {
|
themeStore.setThemeState(theme)
|
||||||
document.getElementsByTagName('html')[0].classList.add('mac')
|
themeStore.collapsedNavigation = collapsed_navigation
|
||||||
} else {
|
themeStore.advancedRendering = advanced_rendering
|
||||||
document.getElementsByTagName('html')[0].classList.add('windows')
|
|
||||||
}
|
|
||||||
|
|
||||||
offline.value = await isOffline()
|
mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
|
||||||
await offline_listener((b) => {
|
if (!telemetry) {
|
||||||
offline.value = b
|
mixpanel_opt_out_tracking()
|
||||||
})
|
}
|
||||||
|
mixpanel_track('Launched', { version, dev, onboarded })
|
||||||
|
|
||||||
await warning_listener((e) =>
|
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
||||||
notificationsWrapper.value.addNotification({
|
|
||||||
title: 'Warning',
|
|
||||||
text: e.message,
|
|
||||||
type: 'warn',
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (showOnboarding.value) {
|
if ((await type()) === 'Darwin') {
|
||||||
onboardingVideo.value.play()
|
document.getElementsByTagName('html')[0].classList.add('mac')
|
||||||
}
|
} else {
|
||||||
},
|
document.getElementsByTagName('html')[0].classList.add('windows')
|
||||||
failure: async (e) => {
|
}
|
||||||
isLoading.value = false
|
|
||||||
failureText.value = e
|
|
||||||
os.value = await getOS()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const confirmClose = async () => {
|
await warning_listener((e) =>
|
||||||
const confirmed = await confirm(
|
notificationsWrapper.value.addNotification({
|
||||||
'An action is currently in progress. Are you sure you want to exit?',
|
title: 'Warning',
|
||||||
{
|
text: e.message,
|
||||||
title: 'Modrinth',
|
type: 'warn',
|
||||||
type: 'warning',
|
}),
|
||||||
},
|
|
||||||
)
|
)
|
||||||
return confirmed
|
|
||||||
|
get_opening_command().then(handleCommand)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stateFailed = ref(false)
|
||||||
|
initialize_state()
|
||||||
|
.then(() => {
|
||||||
|
setupApp().catch((err) => {
|
||||||
|
stateFailed.value = true
|
||||||
|
console.error(err)
|
||||||
|
error.showError(err, null, false, 'state_init')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
stateFailed.value = true
|
||||||
|
console.error('Failed to initialize app', err)
|
||||||
|
error.showError(err, null, false, 'state_init')
|
||||||
|
})
|
||||||
|
|
||||||
const handleClose = async () => {
|
const handleClose = async () => {
|
||||||
if (failureText.value != null) {
|
await saveWindowState(StateFlags.ALL)
|
||||||
await TauriWindow.getCurrent().close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// State should respond immeiately if it's safe to close
|
|
||||||
// If not, code is deadlocked or worse, so wait 2 seconds and then ask the user to confirm closing
|
|
||||||
// (Exception: if the user is changing config directory, which takes control of the state, and it's taking a significant amount of time for some reason)
|
|
||||||
const isSafe = await Promise.race([
|
|
||||||
check_safe_loading_bars_complete(),
|
|
||||||
new Promise((r) => setTimeout(r, 2000)),
|
|
||||||
])
|
|
||||||
if (!isSafe) {
|
|
||||||
const response = await confirmClose()
|
|
||||||
if (!response) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await await_sync()
|
|
||||||
await TauriWindow.getCurrent().close()
|
await TauriWindow.getCurrent().close()
|
||||||
}
|
}
|
||||||
|
|
||||||
const openSupport = async () => {
|
|
||||||
window.__TAURI_INVOKE__('tauri', {
|
|
||||||
__tauriModule: 'Shell',
|
|
||||||
message: {
|
|
||||||
cmd: 'open',
|
|
||||||
path: 'https://discord.gg/modrinth',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
||||||
await handleClose()
|
await handleClose()
|
||||||
})
|
})
|
||||||
@@ -177,19 +144,29 @@ const route = useRoute()
|
|||||||
const isOnBrowse = computed(() => route.path.startsWith('/browse'))
|
const isOnBrowse = computed(() => route.path.startsWith('/browse'))
|
||||||
|
|
||||||
const loading = useLoading()
|
const loading = useLoading()
|
||||||
|
loading.setEnabled(false)
|
||||||
|
|
||||||
const notifications = useNotifications()
|
const notifications = useNotifications()
|
||||||
const notificationsWrapper = ref()
|
const notificationsWrapper = ref()
|
||||||
|
|
||||||
watch(notificationsWrapper, () => {
|
|
||||||
notifications.setNotifs(notificationsWrapper.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
const error = useError()
|
const error = useError()
|
||||||
const errorModal = ref()
|
const errorModal = ref()
|
||||||
|
|
||||||
watch(errorModal, () => {
|
const install = useInstall()
|
||||||
|
const modInstallModal = ref()
|
||||||
|
const installConfirmModal = ref()
|
||||||
|
const incompatibilityWarningModal = ref()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
invoke('show_window')
|
||||||
|
|
||||||
|
notifications.setNotifs(notificationsWrapper.value)
|
||||||
|
|
||||||
error.setErrorModal(errorModal.value)
|
error.setErrorModal(errorModal.value)
|
||||||
|
|
||||||
|
install.setIncompatibilityWarningModal(incompatibilityWarningModal)
|
||||||
|
install.setInstallConfirmModal(installConfirmModal)
|
||||||
|
install.setModInstallModal(modInstallModal)
|
||||||
})
|
})
|
||||||
|
|
||||||
document.querySelector('body').addEventListener('click', function (e) {
|
document.querySelector('body').addEventListener('click', function (e) {
|
||||||
@@ -234,7 +211,10 @@ document.querySelector('body').addEventListener('auxclick', function (e) {
|
|||||||
|
|
||||||
const accounts = ref(null)
|
const accounts = ref(null)
|
||||||
|
|
||||||
command_listener(async (e) => {
|
command_listener(handleCommand)
|
||||||
|
async function handleCommand(e) {
|
||||||
|
if (!e) return
|
||||||
|
|
||||||
if (e.event === 'RunMRPack') {
|
if (e.event === 'RunMRPack') {
|
||||||
// RunMRPack should directly install a local mrpack given a path
|
// RunMRPack should directly install a local mrpack given a path
|
||||||
if (e.path.endsWith('.mrpack')) {
|
if (e.path.endsWith('.mrpack')) {
|
||||||
@@ -247,53 +227,12 @@ command_listener(async (e) => {
|
|||||||
// Other commands are URL-based (deep linking)
|
// Other commands are URL-based (deep linking)
|
||||||
urlModal.value.show(e)
|
urlModal.value.show(e)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="failureText" class="failure dark-mode">
|
<SplashScreen v-if="!stateFailed" ref="splashScreen" data-tauri-drag-region />
|
||||||
<div class="appbar-failure dark-mode">
|
<div v-if="stateInitialized" class="app-container">
|
||||||
<Button v-if="os != 'MacOS'" icon-only @click="TauriWindow.getCurrent().close()">
|
|
||||||
<XIcon />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div class="error-view dark-mode">
|
|
||||||
<Card class="error-text">
|
|
||||||
<div class="label">
|
|
||||||
<h3>
|
|
||||||
<span class="label__title size-card-header">Failed to initialize</span>
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="error-div">
|
|
||||||
Modrinth App failed to load correctly. This may be because of a corrupted file, or because
|
|
||||||
the app is missing crucial files.
|
|
||||||
</div>
|
|
||||||
<div class="error-div">You may be able to fix it one of the following ways:</div>
|
|
||||||
<ul class="error-div">
|
|
||||||
<li>Ennsuring you are connected to the internet, then try restarting the app.</li>
|
|
||||||
<li>Redownloading the app.</li>
|
|
||||||
</ul>
|
|
||||||
<div class="error-div">
|
|
||||||
If it still does not work, you can seek support using the link below. You should provide
|
|
||||||
the following error, as well as any recent launcher logs in the folder below.
|
|
||||||
</div>
|
|
||||||
<div class="error-div">The following error was provided:</div>
|
|
||||||
|
|
||||||
<Card class="error-message">
|
|
||||||
{{ failureText.message }}
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<div class="button-row push-right">
|
|
||||||
<Button @click="showLauncherLogsFolder"><FileIcon />Open launcher logs</Button>
|
|
||||||
|
|
||||||
<Button @click="openSupport"><ChatIcon />Get support</Button>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<SplashScreen v-else-if="isLoading" app-loading />
|
|
||||||
<OnboardingScreen v-else-if="showOnboarding" :finish="() => (showOnboarding = false)" />
|
|
||||||
<div v-else class="container">
|
|
||||||
<div class="nav-container">
|
<div class="nav-container">
|
||||||
<div class="nav-section">
|
<div class="nav-section">
|
||||||
<suspense>
|
<suspense>
|
||||||
@@ -355,16 +294,7 @@ command_listener(async (e) => {
|
|||||||
<Button class="titlebar-button" icon-only @click="() => appWindow.toggleMaximize()">
|
<Button class="titlebar-button" icon-only @click="() => appWindow.toggleMaximize()">
|
||||||
<MaximizeIcon />
|
<MaximizeIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button class="titlebar-button close" icon-only @click="handleClose">
|
||||||
class="titlebar-button close"
|
|
||||||
icon-only
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
saveWindowState(StateFlags.ALL)
|
|
||||||
handleClose()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<XIcon />
|
<XIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</section>
|
</section>
|
||||||
@@ -387,6 +317,9 @@ command_listener(async (e) => {
|
|||||||
<URLConfirmModal ref="urlModal" />
|
<URLConfirmModal ref="urlModal" />
|
||||||
<Notifications ref="notificationsWrapper" />
|
<Notifications ref="notificationsWrapper" />
|
||||||
<ErrorModal ref="errorModal" />
|
<ErrorModal ref="errorModal" />
|
||||||
|
<ModInstallModal ref="modInstallModal" />
|
||||||
|
<IncompatibilityWarningModal ref="incompatibilityWarningModal" />
|
||||||
|
<InstallConfirmModal ref="installConfirmModal" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -438,7 +371,7 @@ command_listener(async (e) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.app-container {
|
||||||
--appbar-height: 3.25rem;
|
--appbar-height: 3.25rem;
|
||||||
--sidebar-width: 4.5rem;
|
--sidebar-width: 4.5rem;
|
||||||
|
|
||||||
@@ -476,53 +409,6 @@ command_listener(async (e) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.failure {
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: var(--color-bg);
|
|
||||||
|
|
||||||
.appbar-failure {
|
|
||||||
display: flex; /* Change to flex to align items horizontally */
|
|
||||||
justify-content: flex-end; /* Align items to the right */
|
|
||||||
height: 3.25rem;
|
|
||||||
//no select
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-view {
|
|
||||||
display: flex; /* Change to flex to align items horizontally */
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
background-color: var(--color-bg);
|
|
||||||
|
|
||||||
color: var(--color-base);
|
|
||||||
|
|
||||||
.card {
|
|
||||||
background-color: var(--color-raised-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-text {
|
|
||||||
display: flex;
|
|
||||||
max-width: 60%;
|
|
||||||
gap: 0.25rem;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.error-div {
|
|
||||||
// spaced out
|
|
||||||
margin: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-message {
|
|
||||||
margin: 0.5rem;
|
|
||||||
background-color: var(--color-button-bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-container {
|
.nav-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -586,55 +472,6 @@ command_listener(async (e) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.instance-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
width: 70%;
|
|
||||||
margin: 0.4rem;
|
|
||||||
|
|
||||||
p:nth-child(1) {
|
|
||||||
font-size: 0.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > p {
|
|
||||||
color: var(--color-base);
|
|
||||||
margin: 0.8rem 0;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
line-height: 0.8125rem;
|
|
||||||
font-weight: 500;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-section {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 4.375rem;
|
|
||||||
|
|
||||||
section {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
text-align: left;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.username {
|
|
||||||
margin-bottom: 0.3rem;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
color: var(--color-contrast);
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-weight: 400;
|
|
||||||
color: var(--color-secondary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-section {
|
.nav-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -645,14 +482,6 @@ command_listener(async (e) => {
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video {
|
|
||||||
margin-top: 2.25rem;
|
|
||||||
width: 100vw;
|
|
||||||
height: calc(100vh - 2.25rem);
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-row {
|
.button-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -664,3 +493,33 @@ command_listener(async (e) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<style>
|
||||||
|
.mac {
|
||||||
|
.nav-container {
|
||||||
|
padding-top: calc(var(--gap-md) + 1.75rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-card,
|
||||||
|
.card-section {
|
||||||
|
top: calc(var(--gap-md) + 1.75rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.windows {
|
||||||
|
.fake-appbar {
|
||||||
|
height: 2.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-controls {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
right: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-card {
|
||||||
|
right: 8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
Before Width: | Height: | Size: 952 B After Width: | Height: | Size: 952 B |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 901 B After Width: | Height: | Size: 901 B |
|
Before Width: | Height: | Size: 757 B After Width: | Height: | Size: 757 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 718 B After Width: | Height: | Size: 718 B |
|
Before Width: | Height: | Size: 962 B After Width: | Height: | Size: 962 B |
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 415 B After Width: | Height: | Size: 415 B |
|
Before Width: | Height: | Size: 274 B After Width: | Height: | Size: 274 B |
|
Before Width: | Height: | Size: 706 B After Width: | Height: | Size: 706 B |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 924 B After Width: | Height: | Size: 924 B |
|
Before Width: | Height: | Size: 881 B After Width: | Height: | Size: 881 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 427 B After Width: | Height: | Size: 427 B |
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
|
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 326 B |
|
Before Width: | Height: | Size: 359 B After Width: | Height: | Size: 359 B |
|
Before Width: | Height: | Size: 322 B After Width: | Height: | Size: 322 B |
|
Before Width: | Height: | Size: 253 B After Width: | Height: | Size: 253 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 461 B After Width: | Height: | Size: 461 B |
|
Before Width: | Height: | Size: 405 B After Width: | Height: | Size: 405 B |
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B |
|
Before Width: | Height: | Size: 309 B After Width: | Height: | Size: 309 B |
BIN
apps/app-frontend/src/assets/loading/cube.png
Normal file
|
After Width: | Height: | Size: 937 KiB |
@@ -1,3 +1,7 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
font-family: var(--font-standard);
|
font-family: var(--font-standard);
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
@@ -69,44 +73,15 @@ input {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mac {
|
|
||||||
.nav-container {
|
|
||||||
padding-top: calc(var(--gap-md) + 1.75rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.account-card,
|
|
||||||
.card-section {
|
|
||||||
top: calc(var(--gap-md) + 1.75rem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.windows {
|
|
||||||
.fake-appbar {
|
|
||||||
height: 2.5rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-controls {
|
|
||||||
display: flex !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-card {
|
|
||||||
right: 8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-card {
|
|
||||||
right: 8rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
scrollbar-width: auto;
|
scrollbar-width: auto;
|
||||||
scrollbar-color: var(--color-button-bg) var(--color-bg);
|
scrollbar-color: var(--color-scrollbar) var(--color-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chrome, Edge, and Safari */
|
/* Chrome, Edge, and Safari */
|
||||||
*::-webkit-scrollbar {
|
*::-webkit-scrollbar {
|
||||||
width: var(--gap-md);
|
width: var(--gap-md);
|
||||||
border: 3px solid var(--color-bg);
|
border: 3px solid var(--color-scrollbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
*::-webkit-scrollbar-track {
|
||||||
@@ -115,7 +90,7 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb {
|
*::-webkit-scrollbar-thumb {
|
||||||
background-color: var(--color-button-bg);
|
background-color: var(--color-scrollbar);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
border: 3px solid var(--color-bg);
|
border: 3px solid var(--color-bg);
|
||||||
}
|
}
|
||||||
@@ -135,3 +110,5 @@ img {
|
|||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import '@modrinth/assets/omorphia.scss';
|
||||||
@@ -9,14 +9,11 @@ import {
|
|||||||
TrashIcon,
|
TrashIcon,
|
||||||
StopCircleIcon,
|
StopCircleIcon,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
Card,
|
|
||||||
DropdownSelect,
|
|
||||||
SearchIcon,
|
SearchIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
Button,
|
} from '@modrinth/assets'
|
||||||
formatCategoryHeader,
|
import { ConfirmModal, Button, Card, DropdownSelect } from '@modrinth/ui'
|
||||||
ModalConfirm,
|
import { formatCategoryHeader } from '@modrinth/utils'
|
||||||
} from 'omorphia'
|
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { useTheming } from '@/store/theme.js'
|
import { useTheming } from '@/store/theme.js'
|
||||||
@@ -130,46 +127,46 @@ const sortBy = ref('Name')
|
|||||||
|
|
||||||
const filteredResults = computed(() => {
|
const filteredResults = computed(() => {
|
||||||
let instances = props.instances.filter((instance) => {
|
let instances = props.instances.filter((instance) => {
|
||||||
return instance.metadata.name.toLowerCase().includes(search.value.toLowerCase())
|
return instance.name.toLowerCase().includes(search.value.toLowerCase())
|
||||||
})
|
})
|
||||||
|
|
||||||
if (sortBy.value === 'Name') {
|
if (sortBy.value === 'Name') {
|
||||||
instances.sort((a, b) => {
|
instances.sort((a, b) => {
|
||||||
return a.metadata.name.localeCompare(b.metadata.name)
|
return a.name.localeCompare(b.name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortBy.value === 'Game version') {
|
if (sortBy.value === 'Game version') {
|
||||||
instances.sort((a, b) => {
|
instances.sort((a, b) => {
|
||||||
return a.metadata.game_version.localeCompare(b.metadata.game_version)
|
return a.game_version.localeCompare(b.game_version)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortBy.value === 'Last played') {
|
if (sortBy.value === 'Last played') {
|
||||||
instances.sort((a, b) => {
|
instances.sort((a, b) => {
|
||||||
return dayjs(b.metadata.last_played ?? 0).diff(dayjs(a.metadata.last_played ?? 0))
|
return dayjs(b.last_played ?? 0).diff(dayjs(a.last_played ?? 0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortBy.value === 'Date created') {
|
if (sortBy.value === 'Date created') {
|
||||||
instances.sort((a, b) => {
|
instances.sort((a, b) => {
|
||||||
return dayjs(b.metadata.date_created).diff(dayjs(a.metadata.date_created))
|
return dayjs(b.date_created).diff(dayjs(a.date_created))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortBy.value === 'Date modified') {
|
if (sortBy.value === 'Date modified') {
|
||||||
instances.sort((a, b) => {
|
instances.sort((a, b) => {
|
||||||
return dayjs(b.metadata.date_modified).diff(dayjs(a.metadata.date_modified))
|
return dayjs(b.date_modified).diff(dayjs(a.date_modified))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filters.value === 'Custom instances') {
|
if (filters.value === 'Custom instances') {
|
||||||
instances = instances.filter((instance) => {
|
instances = instances.filter((instance) => {
|
||||||
return !instance.metadata?.linked_data
|
return !instance.linked_data
|
||||||
})
|
})
|
||||||
} else if (filters.value === 'Downloaded modpacks') {
|
} else if (filters.value === 'Downloaded modpacks') {
|
||||||
instances = instances.filter((instance) => {
|
instances = instances.filter((instance) => {
|
||||||
return instance.metadata?.linked_data
|
return instance.linked_data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +174,7 @@ const filteredResults = computed(() => {
|
|||||||
|
|
||||||
if (group.value === 'Loader') {
|
if (group.value === 'Loader') {
|
||||||
instances.forEach((instance) => {
|
instances.forEach((instance) => {
|
||||||
const loader = formatCategoryHeader(instance.metadata.loader)
|
const loader = formatCategoryHeader(instance.loader)
|
||||||
if (!instanceMap.has(loader)) {
|
if (!instanceMap.has(loader)) {
|
||||||
instanceMap.set(loader, [])
|
instanceMap.set(loader, [])
|
||||||
}
|
}
|
||||||
@@ -186,19 +183,19 @@ const filteredResults = computed(() => {
|
|||||||
})
|
})
|
||||||
} else if (group.value === 'Game version') {
|
} else if (group.value === 'Game version') {
|
||||||
instances.forEach((instance) => {
|
instances.forEach((instance) => {
|
||||||
if (!instanceMap.has(instance.metadata.game_version)) {
|
if (!instanceMap.has(instance.game_version)) {
|
||||||
instanceMap.set(instance.metadata.game_version, [])
|
instanceMap.set(instance.game_version, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceMap.get(instance.metadata.game_version).push(instance)
|
instanceMap.get(instance.game_version).push(instance)
|
||||||
})
|
})
|
||||||
} else if (group.value === 'Category') {
|
} else if (group.value === 'Category') {
|
||||||
instances.forEach((instance) => {
|
instances.forEach((instance) => {
|
||||||
if (instance.metadata.groups.length === 0) {
|
if (instance.groups.length === 0) {
|
||||||
instance.metadata.groups.push('None')
|
instance.groups.push('None')
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const category of instance.metadata.groups) {
|
for (const category of instance.groups) {
|
||||||
if (!instanceMap.has(category)) {
|
if (!instanceMap.has(category)) {
|
||||||
instanceMap.set(category, [])
|
instanceMap.set(category, [])
|
||||||
}
|
}
|
||||||
@@ -233,7 +230,7 @@ const filteredResults = computed(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<ModalConfirm
|
<ConfirmModal
|
||||||
ref="confirmModal"
|
ref="confirmModal"
|
||||||
title="Are you sure you want to delete this instance?"
|
title="Are you sure you want to delete this instance?"
|
||||||
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
||||||
@@ -246,7 +243,7 @@ const filteredResults = computed(() => {
|
|||||||
<div class="iconified-input">
|
<div class="iconified-input">
|
||||||
<SearchIcon />
|
<SearchIcon />
|
||||||
<input v-model="search" type="text" placeholder="Search" class="search-input" />
|
<input v-model="search" type="text" placeholder="Search" class="search-input" />
|
||||||
<Button @click="() => (search = '')">
|
<Button class="r-btn" @click="() => (search = '')">
|
||||||
<XIcon />
|
<XIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -11,28 +11,21 @@ import {
|
|||||||
ExternalIcon,
|
ExternalIcon,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
ModalConfirm,
|
} from '@modrinth/assets'
|
||||||
} from 'omorphia'
|
import { ConfirmModal } from '@modrinth/ui'
|
||||||
import Instance from '@/components/ui/Instance.vue'
|
import Instance from '@/components/ui/Instance.vue'
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import ProjectCard from '@/components/ui/ProjectCard.vue'
|
import ProjectCard from '@/components/ui/ProjectCard.vue'
|
||||||
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
import { get_by_profile_path } from '@/helpers/process.js'
|
||||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
|
||||||
import {
|
|
||||||
get_all_running_profile_paths,
|
|
||||||
get_uuids_by_profile_path,
|
|
||||||
kill_by_uuid,
|
|
||||||
} from '@/helpers/process.js'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { duplicate, remove, run } from '@/helpers/profile.js'
|
import { duplicate, kill, remove, run } from '@/helpers/profile.js'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import { install as pack_install } from '@/helpers/pack.js'
|
|
||||||
import { useTheming } from '@/store/state.js'
|
import { useTheming } from '@/store/state.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
|
import { install as installVersion } from '@/store/install.js'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@@ -58,9 +51,7 @@ const modsRow = ref(null)
|
|||||||
const instanceOptions = ref(null)
|
const instanceOptions = ref(null)
|
||||||
const instanceComponents = ref(null)
|
const instanceComponents = ref(null)
|
||||||
const rows = ref(null)
|
const rows = ref(null)
|
||||||
const confirmModal = ref(null)
|
|
||||||
const deleteConfirmModal = ref(null)
|
const deleteConfirmModal = ref(null)
|
||||||
const modInstallModal = ref(null)
|
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
const currentDeleteInstance = ref(null)
|
const currentDeleteInstance = ref(null)
|
||||||
@@ -90,23 +81,24 @@ const handleInstanceRightClick = async (event, passedInstance) => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const running = await get_all_running_profile_paths().catch(handleError)
|
const runningProcesses = await get_by_profile_path(passedInstance.path).catch(handleError)
|
||||||
|
|
||||||
const options = running.includes(passedInstance.path)
|
const options =
|
||||||
? [
|
runningProcesses.length > 0
|
||||||
{
|
? [
|
||||||
name: 'stop',
|
{
|
||||||
color: 'danger',
|
name: 'stop',
|
||||||
},
|
color: 'danger',
|
||||||
...baseOptions,
|
},
|
||||||
]
|
...baseOptions,
|
||||||
: [
|
]
|
||||||
{
|
: [
|
||||||
name: 'play',
|
{
|
||||||
color: 'primary',
|
name: 'play',
|
||||||
},
|
color: 'primary',
|
||||||
...baseOptions,
|
},
|
||||||
]
|
...baseOptions,
|
||||||
|
]
|
||||||
|
|
||||||
instanceOptions.value.showMenu(event, passedInstance, options)
|
instanceOptions.value.showMenu(event, passedInstance, options)
|
||||||
}
|
}
|
||||||
@@ -130,24 +122,24 @@ const handleProjectClick = (event, passedInstance) => {
|
|||||||
const handleOptionsClick = async (args) => {
|
const handleOptionsClick = async (args) => {
|
||||||
switch (args.option) {
|
switch (args.option) {
|
||||||
case 'play':
|
case 'play':
|
||||||
await run(args.item.path).catch(handleSevereError)
|
await run(args.item.path).catch((err) =>
|
||||||
|
handleSevereError(err, { profilePath: args.item.path }),
|
||||||
|
)
|
||||||
mixpanel_track('InstanceStart', {
|
mixpanel_track('InstanceStart', {
|
||||||
loader: args.item.metadata.loader,
|
loader: args.item.loader,
|
||||||
game_version: args.item.metadata.game_version,
|
game_version: args.item.game_version,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'stop':
|
case 'stop':
|
||||||
for (const u of await get_uuids_by_profile_path(args.item.path).catch(handleError)) {
|
await kill(args.item.path).catch(handleError)
|
||||||
await kill_by_uuid(u).catch(handleError)
|
|
||||||
}
|
|
||||||
mixpanel_track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: args.item.metadata.loader,
|
loader: args.item.loader,
|
||||||
game_version: args.item.metadata.game_version,
|
game_version: args.item.game_version,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'add_content':
|
case 'add_content':
|
||||||
await router.push({
|
await router.push({
|
||||||
path: `/browse/${args.item.metadata.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
path: `/browse/${args.item.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||||
query: { i: args.item.path },
|
query: { i: args.item.path },
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
@@ -170,21 +162,8 @@ const handleOptionsClick = async (args) => {
|
|||||||
await navigator.clipboard.writeText(args.item.path)
|
await navigator.clipboard.writeText(args.item.path)
|
||||||
break
|
break
|
||||||
case 'install': {
|
case 'install': {
|
||||||
const versions = await useFetch(
|
await installVersion(args.item.project_id, null, null, 'ProjectCardContextMenu')
|
||||||
`https://api.modrinth.com/v2/project/${args.item.project_id}/version`,
|
|
||||||
'project versions',
|
|
||||||
)
|
|
||||||
|
|
||||||
if (args.item.project_type === 'modpack') {
|
|
||||||
await pack_install(
|
|
||||||
args.item.project_id,
|
|
||||||
versions[0].id,
|
|
||||||
args.item.title,
|
|
||||||
args.item.icon_url,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
modInstallModal.value.show(args.item.project_id, versions)
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'open_link':
|
case 'open_link':
|
||||||
@@ -228,7 +207,7 @@ onUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ModalConfirm
|
<ConfirmModal
|
||||||
ref="deleteConfirmModal"
|
ref="deleteConfirmModal"
|
||||||
title="Are you sure you want to delete this instance?"
|
title="Are you sure you want to delete this instance?"
|
||||||
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
||||||
@@ -243,7 +222,7 @@ onUnmounted(() => {
|
|||||||
<router-link :to="row.route">{{ row.label }}</router-link>
|
<router-link :to="row.route">{{ row.label }}</router-link>
|
||||||
<ChevronRightIcon />
|
<ChevronRightIcon />
|
||||||
</div>
|
</div>
|
||||||
<section v-if="row.instances[0].metadata" ref="modsRow" class="instances">
|
<section v-if="row.instance" ref="modsRow" class="instances">
|
||||||
<Instance
|
<Instance
|
||||||
v-for="instance in row.instances.slice(0, maxInstancesPerRow)"
|
v-for="instance in row.instances.slice(0, maxInstancesPerRow)"
|
||||||
:key="(instance?.project_id || instance?.id) + instance.install_stage"
|
:key="(instance?.project_id || instance?.id) + instance.install_stage"
|
||||||
@@ -258,8 +237,6 @@ onUnmounted(() => {
|
|||||||
ref="instanceComponents"
|
ref="instanceComponents"
|
||||||
class="item"
|
class="item"
|
||||||
:project="project"
|
:project="project"
|
||||||
:confirm-modal="confirmModal"
|
|
||||||
:mod-install-modal="modInstallModal"
|
|
||||||
@contextmenu.prevent.stop="(event) => handleProjectClick(event, project)"
|
@contextmenu.prevent.stop="(event) => handleProjectClick(event, project)"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
@@ -278,8 +255,6 @@ onUnmounted(() => {
|
|||||||
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
<template #open_link> <GlobeIcon /> Open in Modrinth <ExternalIcon /> </template>
|
||||||
<template #copy_link> <ClipboardCopyIcon /> Copy link </template>
|
<template #copy_link> <ClipboardCopyIcon /> Copy link </template>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
<InstallConfirmModal ref="confirmModal" />
|
|
||||||
<ModInstallModal ref="modInstallModal" />
|
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.content {
|
.content {
|
||||||
@@ -40,10 +40,12 @@ export default defineComponent({
|
|||||||
const loading = useLoading()
|
const loading = useLoading()
|
||||||
|
|
||||||
watch(loading, (newValue) => {
|
watch(loading, (newValue) => {
|
||||||
if (newValue.loading) {
|
if (newValue.barEnabled) {
|
||||||
indicator.start()
|
if (newValue.loading) {
|
||||||
} else {
|
indicator.start()
|
||||||
indicator.finish()
|
} else {
|
||||||
|
indicator.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -59,7 +59,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Avatar, Button, Card, PlusIcon, TrashIcon, LogInIcon } from 'omorphia'
|
import { PlusIcon, TrashIcon, LogInIcon } from '@modrinth/assets'
|
||||||
|
import { Avatar, Button, Card } from '@modrinth/ui'
|
||||||
import { ref, computed, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
|
import { ref, computed, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
|
||||||
import {
|
import {
|
||||||
users,
|
users,
|
||||||
63
apps/app-frontend/src/components/ui/AddContentButton.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { DropdownIcon, FolderOpenIcon, SearchIcon } from '@modrinth/assets'
|
||||||
|
import { Button, OverflowMenu } from '@modrinth/ui'
|
||||||
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
|
import { add_project_from_path } from '@/helpers/profile.js'
|
||||||
|
import { handleError } from '@/store/notifications.js'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
instance: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const handleAddContentFromFile = async () => {
|
||||||
|
const newProject = await open({ multiple: true })
|
||||||
|
if (!newProject) return
|
||||||
|
|
||||||
|
for (const project of newProject) {
|
||||||
|
await add_project_from_path(props.instance.path, project).catch(handleError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearchContent = async () => {
|
||||||
|
await router.push({
|
||||||
|
path: `/browse/${props.instance.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||||
|
query: { i: props.instance.path },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="joined-buttons">
|
||||||
|
<Button color="primary" @click="handleSearchContent"><SearchIcon /> Add content </Button>
|
||||||
|
|
||||||
|
<OverflowMenu
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
id: 'search',
|
||||||
|
action: handleSearchContent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'from_file',
|
||||||
|
action: handleAddContentFromFile,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
class="btn btn-primary btn-dropdown-animation icon-only"
|
||||||
|
>
|
||||||
|
<DropdownIcon />
|
||||||
|
<template #search>
|
||||||
|
<SearchIcon />
|
||||||
|
<span class="no-wrap"> Search </span>
|
||||||
|
</template>
|
||||||
|
<template #from_file>
|
||||||
|
<FolderOpenIcon />
|
||||||
|
<span class="no-wrap"> Add from file </span>
|
||||||
|
</template>
|
||||||
|
</OverflowMenu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -31,7 +31,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ChevronRightIcon, Button, ChevronLeftIcon } from 'omorphia'
|
import { ChevronRightIcon, ChevronLeftIcon } from '@modrinth/assets'
|
||||||
|
import { Button } from '@modrinth/ui'
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
@@ -60,9 +60,9 @@ defineExpose({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const isLinkedData = (item) => {
|
const isLinkedData = (item) => {
|
||||||
if (item.instance != undefined && item.instance.metadata.linked_data) {
|
if (item.instance != undefined && item.instance.linked_data) {
|
||||||
return true
|
return true
|
||||||
} else if (item.metadata != undefined && item.metadata.linked_data) {
|
} else if (item != undefined && item.linked_data) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Modal, XIcon, IssuesIcon, LogInIcon } from 'omorphia'
|
import { XIcon, HammerIcon, LogInIcon, UpdatedIcon } from '@modrinth/assets'
|
||||||
|
import { Modal } from '@modrinth/ui'
|
||||||
import { ChatIcon } from '@/assets/icons'
|
import { ChatIcon } from '@/assets/icons'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
|
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import mixpanel from 'mixpanel-browser'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
|
import { cancel_directory_change } from '@/helpers/settings.js'
|
||||||
|
import { install } from '@/helpers/profile.js'
|
||||||
|
|
||||||
const errorModal = ref()
|
const errorModal = ref()
|
||||||
const error = ref()
|
const error = ref()
|
||||||
|
const closable = ref(true)
|
||||||
|
|
||||||
const title = ref('An error occurred')
|
const title = ref('An error occurred')
|
||||||
const errorType = ref('unknown')
|
const errorType = ref('unknown')
|
||||||
@@ -16,7 +20,9 @@ const supportLink = ref('https://support.modrinth.com')
|
|||||||
const metadata = ref({})
|
const metadata = ref({})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
async show(errorVal) {
|
async show(errorVal, context, canClose = true, source = null) {
|
||||||
|
closable.value = canClose
|
||||||
|
|
||||||
if (errorVal.message && errorVal.message.includes('Minecraft authentication error:')) {
|
if (errorVal.message && errorVal.message.includes('Minecraft authentication error:')) {
|
||||||
title.value = 'Unable to sign in to Minecraft'
|
title.value = 'Unable to sign in to Minecraft'
|
||||||
errorType.value = 'minecraft_auth'
|
errorType.value = 'minecraft_auth'
|
||||||
@@ -36,6 +42,27 @@ defineExpose({
|
|||||||
title.value = 'Sign in to Minecraft'
|
title.value = 'Sign in to Minecraft'
|
||||||
errorType.value = 'minecraft_sign_in'
|
errorType.value = 'minecraft_sign_in'
|
||||||
supportLink.value = 'https://support.modrinth.com'
|
supportLink.value = 'https://support.modrinth.com'
|
||||||
|
} else if (errorVal.message && errorVal.message.includes('Move directory error:')) {
|
||||||
|
title.value = 'Could not change app directory'
|
||||||
|
errorType.value = 'directory_move'
|
||||||
|
supportLink.value = 'https://support.modrinth.com'
|
||||||
|
|
||||||
|
if (errorVal.message.includes('directory is not writeable')) {
|
||||||
|
metadata.value.readOnly = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorVal.message.includes('Not enough space')) {
|
||||||
|
metadata.value.notEnoughSpace = true
|
||||||
|
}
|
||||||
|
} else if (errorVal.message && errorVal.message.includes('No loader version selected for')) {
|
||||||
|
title.value = 'No loader selected'
|
||||||
|
errorType.value = 'no_loader_version'
|
||||||
|
supportLink.value = 'https://support.modrinth.com'
|
||||||
|
metadata.value.profilePath = context.profilePath
|
||||||
|
} else if (source === 'state_init') {
|
||||||
|
title.value = 'Error initializing Modrinth App'
|
||||||
|
errorType.value = 'state_init'
|
||||||
|
supportLink.value = 'https://support.modrinth.com'
|
||||||
} else {
|
} else {
|
||||||
title.value = 'An error occurred'
|
title.value = 'An error occurred'
|
||||||
errorType.value = 'unknown'
|
errorType.value = 'unknown'
|
||||||
@@ -66,10 +93,35 @@ async function loginMinecraft() {
|
|||||||
handleSevereError(err)
|
handleSevereError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function cancelDirectoryChange() {
|
||||||
|
try {
|
||||||
|
await cancel_directory_change()
|
||||||
|
window.location.reload()
|
||||||
|
} catch (err) {
|
||||||
|
handleError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function retryDirectoryChange() {
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadingRepair = ref(false)
|
||||||
|
async function repairInstance() {
|
||||||
|
loadingRepair.value = true
|
||||||
|
try {
|
||||||
|
await install(metadata.value.profilePath, false)
|
||||||
|
errorModal.value.hide()
|
||||||
|
} catch (err) {
|
||||||
|
handleSevereError(err)
|
||||||
|
}
|
||||||
|
loadingRepair.value = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal ref="errorModal" :header="title">
|
<Modal ref="errorModal" :header="title" :closable="closable">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="markdown-body">
|
<div class="markdown-body">
|
||||||
<template v-if="errorType === 'minecraft_auth'">
|
<template v-if="errorType === 'minecraft_auth'">
|
||||||
@@ -124,30 +176,40 @@ async function loginMinecraft() {
|
|||||||
<LogInIcon /> Try signing in again
|
<LogInIcon /> Try signing in again
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
</template>
|
||||||
<p>
|
<template v-if="errorType === 'directory_move'">
|
||||||
If nothing is working and you need help, visit
|
<template v-if="metadata.readOnly">
|
||||||
<a :href="supportLink">our support page</a>
|
<h3>Change directory permissions</h3>
|
||||||
and start a chat using the widget in the bottom right and we will be more than happy to
|
<p>
|
||||||
assist! Make sure to provide the following debug information to the agent:
|
It looks like the Modrinth App is unable to write to the directory you selected.
|
||||||
</p>
|
Please adjust the permissions of the directory and try again or cancel the directory
|
||||||
<details>
|
change.
|
||||||
<summary>Debug information</summary>
|
</p>
|
||||||
{{ error.message ?? error }}
|
</template>
|
||||||
</details>
|
<template v-else-if="metadata.notEnoughSpace">
|
||||||
|
<h3>Not enough space</h3>
|
||||||
|
<p>
|
||||||
|
It looks like there is not enough space on the disk containing the dirctory you
|
||||||
|
selected Please free up some space and try again or cancel the directory change.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<p>
|
||||||
|
The Modrinth App is unable to migrate to the new directory you selected. Please
|
||||||
|
contact support for help or cancel the directory change.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="cta-button">
|
||||||
|
<button class="btn" @click="retryDirectoryChange">
|
||||||
|
<UpdatedIcon /> Retry directory change
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-danger" @click="cancelDirectoryChange">
|
||||||
|
<XIcon /> Cancel directory change
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div v-else-if="errorType === 'minecraft_sign_in'">
|
<div v-else-if="errorType === 'minecraft_sign_in'">
|
||||||
<div class="warning-banner">
|
|
||||||
<div class="warning-banner__title">
|
|
||||||
<IssuesIcon />
|
|
||||||
<span>Installed the app before April 23rd, 2024?</span>
|
|
||||||
</div>
|
|
||||||
<div class="warning-banner__description">
|
|
||||||
Modrinth has updated our sign-in workflow to allow for better stability, security, and
|
|
||||||
performance. You must sign in again so your credentials can be upgraded to this new
|
|
||||||
flow.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p>
|
<p>
|
||||||
To play this instance, you must sign in through Microsoft below. If you don't have a
|
To play this instance, you must sign in through Microsoft below. If you don't have a
|
||||||
Minecraft account, you can purchase the game on the
|
Minecraft account, you can purchase the game on the
|
||||||
@@ -161,13 +223,53 @@ async function loginMinecraft() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-else-if="errorType === 'state_init'">
|
||||||
|
<p>
|
||||||
|
Modrinth App failed to load correctly. This may be because of a corrupted file, or
|
||||||
|
because the app is missing crucial files.
|
||||||
|
</p>
|
||||||
|
<p>You may be able to fix it through one of the following ways:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Ennsuring you are connected to the internet, then try restarting the app.</li>
|
||||||
|
<li>Redownloading the app.</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="errorType === 'no_loader_version'">
|
||||||
|
<p>The Modrinth App failed to find the loader version for this instance.</p>
|
||||||
|
<p>To resolve this, you need to repair the instance. Click the button below to do so.</p>
|
||||||
|
<div class="cta-button">
|
||||||
|
<button class="btn btn-primary" :disabled="loadingRepair" @click="repairInstance">
|
||||||
|
<HammerIcon /> Repair instance
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ error.message ?? error }}
|
{{ error.message ?? error }}
|
||||||
</template>
|
</template>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
errorType === 'directory_move' ||
|
||||||
|
errorType === 'minecraft_auth' ||
|
||||||
|
errorType === 'state_init' ||
|
||||||
|
errorType === 'no_loader_version'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<hr />
|
||||||
|
<p>
|
||||||
|
If nothing is working and you need help, visit
|
||||||
|
<a :href="supportLink">our support page</a>
|
||||||
|
and start a chat using the widget in the bottom right and we will be more than happy to
|
||||||
|
assist! Make sure to provide the following debug information to the agent:
|
||||||
|
</p>
|
||||||
|
<details>
|
||||||
|
<summary>Debug information</summary>
|
||||||
|
{{ error.message ?? error }}
|
||||||
|
</details>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group push-right">
|
<div class="input-group push-right">
|
||||||
<a :href="supportLink" class="btn" @click="errorModal.hide()"><ChatIcon /> Get support</a>
|
<a :href="supportLink" class="btn" @click="errorModal.hide()"><ChatIcon /> Get support</a>
|
||||||
<button class="btn" @clicdck="errorModal.hide()"><XIcon /> Close</button>
|
<button v-if="closable" class="btn" @click="errorModal.hide()"><XIcon /> Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -190,6 +292,7 @@ async function loginMinecraft() {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning-banner {
|
.warning-banner {
|
||||||
@@ -222,4 +325,8 @@ async function loginMinecraft() {
|
|||||||
gap: var(--gap-md);
|
gap: var(--gap-md);
|
||||||
padding: var(--gap-lg);
|
padding: var(--gap-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.markdown-body {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Button, Checkbox, Modal, XIcon, PlusIcon } from 'omorphia'
|
import { XIcon, PlusIcon } from '@modrinth/assets'
|
||||||
|
import { Button, Checkbox, Modal } from '@modrinth/ui'
|
||||||
import { PackageIcon, VersionIcon } from '@/assets/icons'
|
import { PackageIcon, VersionIcon } from '@/assets/icons'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js'
|
import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js'
|
||||||
@@ -22,7 +23,7 @@ defineExpose({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const exportModal = ref(null)
|
const exportModal = ref(null)
|
||||||
const nameInput = ref(props.instance.metadata.name)
|
const nameInput = ref(props.instance.name)
|
||||||
const exportDescription = ref('')
|
const exportDescription = ref('')
|
||||||
const versionInput = ref('1.0.0')
|
const versionInput = ref('1.0.0')
|
||||||
const files = ref([])
|
const files = ref([])
|
||||||
@@ -49,9 +50,9 @@ const initFiles = async () => {
|
|||||||
disabled:
|
disabled:
|
||||||
folder === 'profile.json' ||
|
folder === 'profile.json' ||
|
||||||
folder.startsWith('modrinth_logs') ||
|
folder.startsWith('modrinth_logs') ||
|
||||||
folder.startsWith('.fabric') ||
|
folder.startsWith('.fabric'),
|
||||||
folder.includes('.DS_Store'),
|
|
||||||
}))
|
}))
|
||||||
|
.filter((pathData) => !pathData.path.includes('.DS_Store'))
|
||||||
.forEach((pathData) => {
|
.forEach((pathData) => {
|
||||||
const parent = pathData.path.split(sep).slice(0, -1).join(sep)
|
const parent = pathData.path.split(sep).slice(0, -1).join(sep)
|
||||||
if (parent !== '') {
|
if (parent !== '') {
|
||||||
@@ -112,7 +113,7 @@ const exportPack = async () => {
|
|||||||
<div class="iconified-input">
|
<div class="iconified-input">
|
||||||
<PackageIcon />
|
<PackageIcon />
|
||||||
<input v-model="nameInput" type="text" placeholder="Modpack name" class="input" />
|
<input v-model="nameInput" type="text" placeholder="Modpack name" class="input" />
|
||||||
<Button @click="nameInput = ''">
|
<Button class="r-btn" @click="nameInput = ''">
|
||||||
<XIcon />
|
<XIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,7 +123,7 @@ const exportPack = async () => {
|
|||||||
<div class="iconified-input">
|
<div class="iconified-input">
|
||||||
<VersionIcon />
|
<VersionIcon />
|
||||||
<input v-model="versionInput" type="text" placeholder="1.0.0" class="input" />
|
<input v-model="versionInput" type="text" placeholder="1.0.0" class="input" />
|
||||||
<Button @click="versionInput = ''">
|
<Button class="r-btn" @click="versionInput = ''">
|
||||||
<XIcon />
|
<XIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
259
apps/app-frontend/src/components/ui/Instance.vue
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onUnmounted, ref, computed } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { StopCircleIcon, PlayIcon } from '@modrinth/assets'
|
||||||
|
import { Card, Avatar, AnimatedLogo } from '@modrinth/ui'
|
||||||
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
|
import { kill, run } from '@/helpers/profile'
|
||||||
|
import { get_by_profile_path } from '@/helpers/process'
|
||||||
|
import { process_listener } from '@/helpers/events'
|
||||||
|
import { handleError } from '@/store/state.js'
|
||||||
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
import { handleSevereError } from '@/store/error.js'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
instance: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const playing = ref(false)
|
||||||
|
|
||||||
|
const modLoading = computed(() => props.instance.install_stage !== 'installed')
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const seeInstance = async () => {
|
||||||
|
await router.push(`/instance/${encodeURIComponent(props.instance.path)}/`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkProcess = async () => {
|
||||||
|
const runningProcesses = await get_by_profile_path(props.instance.path).catch(handleError)
|
||||||
|
|
||||||
|
playing.value = runningProcesses.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const play = async (e, context) => {
|
||||||
|
e?.stopPropagation()
|
||||||
|
modLoading.value = true
|
||||||
|
await run(props.instance.path).catch((err) =>
|
||||||
|
handleSevereError(err, { profilePath: props.instance.path }),
|
||||||
|
)
|
||||||
|
modLoading.value = false
|
||||||
|
|
||||||
|
mixpanel_track('InstancePlay', {
|
||||||
|
loader: props.instance.loader,
|
||||||
|
game_version: props.instance.game_version,
|
||||||
|
source: context,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const stop = async (e, context) => {
|
||||||
|
e?.stopPropagation()
|
||||||
|
playing.value = false
|
||||||
|
|
||||||
|
await kill(props.instance.path).catch(handleError)
|
||||||
|
|
||||||
|
mixpanel_track('InstanceStop', {
|
||||||
|
loader: props.instance.loader,
|
||||||
|
game_version: props.instance.game_version,
|
||||||
|
source: context,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const openFolder = async () => {
|
||||||
|
await showProfileInFolder(props.instance.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addContent = async () => {
|
||||||
|
await router.push({
|
||||||
|
path: `/browse/${props.instance.loader === 'vanilla' ? 'datapack' : 'mod'}`,
|
||||||
|
query: { i: props.instance.path },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
play,
|
||||||
|
stop,
|
||||||
|
seeInstance,
|
||||||
|
openFolder,
|
||||||
|
addContent,
|
||||||
|
instance: props.instance,
|
||||||
|
})
|
||||||
|
|
||||||
|
const unlisten = await process_listener((e) => {
|
||||||
|
if (e.event === 'finished' && e.profile_path_id === props.instance.path) playing.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => unlisten())
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="instance">
|
||||||
|
<Card class="instance-card-item button-base" @click="seeInstance" @mouseenter="checkProcess">
|
||||||
|
<Avatar
|
||||||
|
size="lg"
|
||||||
|
:src="props.instance.icon_path ? convertFileSrc(props.instance.icon_path) : null"
|
||||||
|
alt="Mod card"
|
||||||
|
class="mod-image"
|
||||||
|
/>
|
||||||
|
<div class="project-info">
|
||||||
|
<p class="title">{{ props.instance.name }}</p>
|
||||||
|
<p
|
||||||
|
v-if="
|
||||||
|
props.instance.install_stage === 'installing' ||
|
||||||
|
props.instance.install_stage === 'not_installed' ||
|
||||||
|
props.instance.install_stage === 'pack_installing'
|
||||||
|
"
|
||||||
|
class="description"
|
||||||
|
>
|
||||||
|
Installing...
|
||||||
|
</p>
|
||||||
|
<p v-else class="description">
|
||||||
|
{{ props.instance.loader }}
|
||||||
|
{{ props.instance.game_version }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<div
|
||||||
|
v-if="playing === true"
|
||||||
|
class="stop cta button-base"
|
||||||
|
@click="(e) => stop(e, 'InstanceCard')"
|
||||||
|
@mousehover="checkProcess"
|
||||||
|
>
|
||||||
|
<StopCircleIcon />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="modLoading === true && playing === false" class="cta loading-cta">
|
||||||
|
<AnimatedLogo class="loading-indicator" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="install cta button-base" @click="(e) => play(e, 'InstanceCard')">
|
||||||
|
<PlayIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.loading-indicator {
|
||||||
|
width: 2.5rem !important;
|
||||||
|
height: 2.5rem !important;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 2.5rem !important;
|
||||||
|
height: 2.5rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.instance {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.cta {
|
||||||
|
opacity: 1;
|
||||||
|
bottom: calc(var(--gap-md) + 4.25rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
z-index: 1;
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
right: calc(var(--gap-md) * 2);
|
||||||
|
bottom: 3.25rem;
|
||||||
|
opacity: 0;
|
||||||
|
transition:
|
||||||
|
0.2s ease-in-out bottom,
|
||||||
|
0.2s ease-in-out opacity,
|
||||||
|
0.1s ease-in-out filter !important;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: var(--shadow-floating);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: var(--color-accent-contrast);
|
||||||
|
width: 1.5rem !important;
|
||||||
|
height: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.install {
|
||||||
|
background: var(--color-brand);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.stop {
|
||||||
|
background: var(--color-red);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.loading-cta {
|
||||||
|
background: hsl(220, 11%, 10%) !important;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.instance-card-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: var(--gap-md);
|
||||||
|
transition: 0.1s ease-in-out all !important; /* overrides Omorphia defaults */
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
.mod-image {
|
||||||
|
--size: 100%;
|
||||||
|
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
max-width: unset !important;
|
||||||
|
max-height: unset !important;
|
||||||
|
aspect-ratio: 1 / 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-info {
|
||||||
|
margin-top: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: var(--color-contrast);
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 110%;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: var(--color-base);
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.775rem;
|
||||||
|
line-height: 125%;
|
||||||
|
margin: 0.25rem 0 0;
|
||||||
|
text-transform: capitalize;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
placeholder="Path to launcher"
|
placeholder="Path to launcher"
|
||||||
@change="setPath"
|
@change="setPath"
|
||||||
/>
|
/>
|
||||||
<Button @click="() => (selectedLauncherPath = '')">
|
<Button class="r-btn" @click="() => (selectedLauncherPath = '')">
|
||||||
<XIcon />
|
<XIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -198,32 +198,22 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import {
|
||||||
Avatar,
|
|
||||||
Button,
|
|
||||||
Chips,
|
|
||||||
Modal,
|
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
UploadIcon,
|
UploadIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
CodeIcon,
|
CodeIcon,
|
||||||
Checkbox,
|
|
||||||
FolderOpenIcon,
|
FolderOpenIcon,
|
||||||
InfoIcon,
|
InfoIcon,
|
||||||
FolderSearchIcon,
|
FolderSearchIcon,
|
||||||
UpdatedIcon,
|
UpdatedIcon,
|
||||||
} from 'omorphia'
|
} from '@modrinth/assets'
|
||||||
|
import { Avatar, Button, Chips, Modal, Checkbox } from '@modrinth/ui'
|
||||||
import { computed, onUnmounted, ref, shallowRef } from 'vue'
|
import { computed, onUnmounted, ref, shallowRef } from 'vue'
|
||||||
import { get_loaders } from '@/helpers/tags'
|
import { get_loaders } from '@/helpers/tags'
|
||||||
import { create } from '@/helpers/profile'
|
import { create } from '@/helpers/profile'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { tauri } from '@tauri-apps/api'
|
import { tauri } from '@tauri-apps/api'
|
||||||
import {
|
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
|
||||||
get_game_versions,
|
|
||||||
get_fabric_versions,
|
|
||||||
get_forge_versions,
|
|
||||||
get_quilt_versions,
|
|
||||||
get_neoforge_versions,
|
|
||||||
} from '@/helpers/metadata'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import Multiselect from 'vue-multiselect'
|
import Multiselect from 'vue-multiselect'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
@@ -308,10 +298,10 @@ const [
|
|||||||
all_game_versions,
|
all_game_versions,
|
||||||
loaders,
|
loaders,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
get_fabric_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('fabric').then(shallowRef).catch(handleError),
|
||||||
get_forge_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('forge').then(shallowRef).catch(handleError),
|
||||||
get_quilt_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('quilt').then(shallowRef).catch(handleError),
|
||||||
get_neoforge_versions().then(shallowRef).catch(handleError),
|
get_loader_versions('neo').then(shallowRef).catch(handleError),
|
||||||
get_game_versions().then(shallowRef).catch(handleError),
|
get_game_versions().then(shallowRef).catch(handleError),
|
||||||
get_loaders()
|
get_loaders()
|
||||||
.then((value) =>
|
.then((value) =>
|
||||||
@@ -35,7 +35,8 @@
|
|||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Modal, PlusIcon, CheckIcon, Button, XIcon } from 'omorphia'
|
import { PlusIcon, CheckIcon, XIcon } from '@modrinth/assets'
|
||||||
|
import { Modal, Button } from '@modrinth/ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { find_filtered_jres } from '@/helpers/jre.js'
|
import { find_filtered_jres } from '@/helpers/jre.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
@@ -52,9 +53,6 @@ defineExpose({
|
|||||||
show: async (version, currentSelectedJava) => {
|
show: async (version, currentSelectedJava) => {
|
||||||
chosenInstallOptions.value = await find_filtered_jres(version).catch(handleError)
|
chosenInstallOptions.value = await find_filtered_jres(version).catch(handleError)
|
||||||
|
|
||||||
console.log(chosenInstallOptions.value)
|
|
||||||
console.log(version)
|
|
||||||
|
|
||||||
currentSelected.value = currentSelectedJava
|
currentSelected.value = currentSelectedJava
|
||||||
if (!currentSelected.value) {
|
if (!currentSelected.value) {
|
||||||
currentSelected.value = { path: '', version: '' }
|
currentSelected.value = { path: '', version: '' }
|
||||||
@@ -53,14 +53,14 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import {
|
||||||
Button,
|
|
||||||
SearchIcon,
|
SearchIcon,
|
||||||
PlayIcon,
|
PlayIcon,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
FolderSearchIcon,
|
FolderSearchIcon,
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
} from 'omorphia'
|
} from '@modrinth/assets'
|
||||||
|
import { Button } from '@modrinth/ui'
|
||||||
import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/helpers/jre.js'
|
import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/helpers/jre.js'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
@@ -162,7 +162,6 @@ async function reinstallJava() {
|
|||||||
const path = await auto_install_java(props.version).catch(handleError)
|
const path = await auto_install_java(props.version).catch(handleError)
|
||||||
let result = await get_jre(path)
|
let result = await get_jre(path)
|
||||||
|
|
||||||
console.log('java result ' + result)
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = {
|
result = {
|
||||||
path: path,
|
path: path,
|
||||||
@@ -205,6 +204,10 @@ async function reinstallJava() {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.test-success {
|
.test-success {
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Button, Modal, CheckIcon, Badge } from 'omorphia'
|
import { CheckIcon } from '@modrinth/assets'
|
||||||
|
import { Button, Modal, Badge } from '@modrinth/ui'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useTheming } from '@/store/theme'
|
import { useTheming } from '@/store/theme'
|
||||||
import { update_managed_modrinth_version } from '@/helpers/profile'
|
import { update_managed_modrinth_version } from '@/helpers/profile'
|
||||||
@@ -28,7 +29,7 @@ const filteredVersions = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const modpackVersionModal = ref(null)
|
const modpackVersionModal = ref(null)
|
||||||
const installedVersion = computed(() => props.instance?.metadata?.linked_data?.version_id)
|
const installedVersion = computed(() => props.instance?.linked_data?.version_id)
|
||||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||||
const inProgress = ref(false)
|
const inProgress = ref(false)
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ const switchVersion = async (versionId) => {
|
|||||||
:noblur="!themeStore.advancedRendering"
|
:noblur="!themeStore.advancedRendering"
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<Card v-if="instance.metadata.linked_data" class="mod-card">
|
<Card v-if="instance.linked_data" class="mod-card">
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<div class="table-row with-columns table-head">
|
<div class="table-row with-columns table-head">
|
||||||
<div class="table-cell table-text download-cell" />
|
<div class="table-cell table-text download-cell" />
|
||||||
@@ -1,22 +1,13 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import { Card, Avatar, Button } from '@modrinth/ui'
|
||||||
Card,
|
import { DownloadIcon, HeartIcon, CalendarIcon } from '@modrinth/assets'
|
||||||
Avatar,
|
import { formatNumber, formatCategory } from '@modrinth/utils'
|
||||||
Button,
|
|
||||||
formatNumber,
|
|
||||||
formatCategory,
|
|
||||||
DownloadIcon,
|
|
||||||
HeartIcon,
|
|
||||||
CalendarIcon,
|
|
||||||
} from 'omorphia'
|
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
import { list } from '@/helpers/profile.js'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
|
||||||
import { install as pack_install } from '@/helpers/pack.js'
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -29,18 +20,6 @@ const props = defineProps({
|
|||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
confirmModal: {
|
|
||||||
type: Object,
|
|
||||||
default() {
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
modInstallModal: {
|
|
||||||
type: Object,
|
|
||||||
default() {
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const toColor = computed(() => {
|
const toColor = computed(() => {
|
||||||
@@ -72,40 +51,15 @@ const toTransparent = computed(() => {
|
|||||||
const install = async (e) => {
|
const install = async (e) => {
|
||||||
e?.stopPropagation()
|
e?.stopPropagation()
|
||||||
installing.value = true
|
installing.value = true
|
||||||
const versions = await useFetch(
|
await installVersion(
|
||||||
`https://api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
props.project.project_id,
|
||||||
'project versions',
|
null,
|
||||||
)
|
props.instance ? props.instance.path : null,
|
||||||
|
'ProjectCard',
|
||||||
if (props.project.project_type === 'modpack') {
|
() => {
|
||||||
const packs = Object.values(await list(true).catch(handleError))
|
|
||||||
|
|
||||||
if (
|
|
||||||
packs.length === 0 ||
|
|
||||||
!packs
|
|
||||||
.map((value) => value.metadata)
|
|
||||||
.find((pack) => pack.linked_data?.project_id === props.project.project_id)
|
|
||||||
) {
|
|
||||||
installing.value = true
|
|
||||||
await pack_install(
|
|
||||||
props.project.project_id,
|
|
||||||
versions[0].id,
|
|
||||||
props.project.title,
|
|
||||||
props.project.icon_url,
|
|
||||||
).catch(handleError)
|
|
||||||
installing.value = false
|
installing.value = false
|
||||||
} else
|
},
|
||||||
props.confirmModal.show(
|
)
|
||||||
props.project.project_id,
|
|
||||||
versions[0].id,
|
|
||||||
props.project.title,
|
|
||||||
props.project.icon_url,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
props.modInstallModal.show(props.project.project_id, versions)
|
|
||||||
}
|
|
||||||
|
|
||||||
installing.value = false
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
23
apps/app-frontend/src/components/ui/PromotionWrapper.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Promotion } from '@modrinth/ui'
|
||||||
|
import { get as getCreds } from '@/helpers/mr_auth.js'
|
||||||
|
import { handleError } from '@/store/notifications.js'
|
||||||
|
import { get_user } from '@/helpers/cache.js'
|
||||||
|
|
||||||
|
const showAd = ref(true)
|
||||||
|
|
||||||
|
const creds = await getCreds().catch(handleError)
|
||||||
|
if (creds && creds.user_id) {
|
||||||
|
const user = await get_user(creds.user_id).catch(handleError)
|
||||||
|
|
||||||
|
const MIDAS_BITFLAG = 1 << 0
|
||||||
|
if (user && (user.badges & MIDAS_BITFLAG) === MIDAS_BITFLAG) {
|
||||||
|
showAd.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Promotion v-if="showAd" :external="false" query-param="?r=launcher" />
|
||||||
|
</template>
|
||||||
@@ -15,15 +15,15 @@
|
|||||||
</Button>
|
</Button>
|
||||||
<div v-if="offline" class="status">
|
<div v-if="offline" class="status">
|
||||||
<span class="circle stopped" />
|
<span class="circle stopped" />
|
||||||
<div class="running-text clickable" @click="refreshInternet()">
|
<div class="running-text">
|
||||||
<span> Offline </span>
|
<span> Offline </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectedProfile" class="status">
|
<div v-if="selectedProcess" class="status">
|
||||||
<span class="circle running" />
|
<span class="circle running" />
|
||||||
<div ref="profileButton" class="running-text">
|
<div ref="profileButton" class="running-text">
|
||||||
<router-link :to="`/instance/${encodeURIComponent(selectedProfile.path)}`">
|
<router-link :to="`/instance/${encodeURIComponent(selectedProcess.profile.path)}`">
|
||||||
{{ selectedProfile.metadata.name }}
|
{{ selectedProcess.profile.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<div
|
<div
|
||||||
v-if="currentProcesses.length > 1"
|
v-if="currentProcesses.length > 1"
|
||||||
@@ -34,7 +34,12 @@
|
|||||||
<DropdownIcon />
|
<DropdownIcon />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button v-tooltip="'Stop instance'" icon-only class="icon-button stop" @click="stop()">
|
<Button
|
||||||
|
v-tooltip="'Stop instance'"
|
||||||
|
icon-only
|
||||||
|
class="icon-button stop"
|
||||||
|
@click="stop(selectedProcess)"
|
||||||
|
>
|
||||||
<StopCircleIcon />
|
<StopCircleIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click="goToTerminal()">
|
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click="goToTerminal()">
|
||||||
@@ -75,17 +80,17 @@
|
|||||||
class="profile-card"
|
class="profile-card"
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
v-for="profile in currentProcesses"
|
v-for="process in currentProcesses"
|
||||||
:key="profile.id"
|
:key="process.uuid"
|
||||||
class="profile-button"
|
class="profile-button"
|
||||||
@click="selectProfile(profile)"
|
@click="selectProcess(process)"
|
||||||
>
|
>
|
||||||
<div class="text"><span class="circle running" /> {{ profile.metadata.name }}</div>
|
<div class="text"><span class="circle running" /> {{ process.profile.name }}</div>
|
||||||
<Button
|
<Button
|
||||||
v-tooltip="'Stop instance'"
|
v-tooltip="'Stop instance'"
|
||||||
icon-only
|
icon-only
|
||||||
class="icon-button stop"
|
class="icon-button stop"
|
||||||
@click.stop="stop(profile.path)"
|
@click.stop="stop(process)"
|
||||||
>
|
>
|
||||||
<StopCircleIcon />
|
<StopCircleIcon />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -93,7 +98,7 @@
|
|||||||
v-tooltip="'View logs'"
|
v-tooltip="'View logs'"
|
||||||
icon-only
|
icon-only
|
||||||
class="icon-button"
|
class="icon-button"
|
||||||
@click.stop="goToTerminal(profile.path)"
|
@click.stop="goToTerminal(process.profile.path)"
|
||||||
>
|
>
|
||||||
<TerminalSquareIcon />
|
<TerminalSquareIcon />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -103,28 +108,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import { DownloadIcon, StopCircleIcon, TerminalSquareIcon, DropdownIcon } from '@modrinth/assets'
|
||||||
Button,
|
import { Button, Card } from '@modrinth/ui'
|
||||||
DownloadIcon,
|
|
||||||
Card,
|
|
||||||
StopCircleIcon,
|
|
||||||
TerminalSquareIcon,
|
|
||||||
DropdownIcon,
|
|
||||||
} from 'omorphia'
|
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import {
|
import { get_all as getRunningProcesses, kill as killProcess } from '@/helpers/process'
|
||||||
get_all_running_profiles as getRunningProfiles,
|
import { loading_listener, process_listener } from '@/helpers/events'
|
||||||
kill_by_uuid as killProfile,
|
|
||||||
get_uuids_by_profile_path as getProfileProcesses,
|
|
||||||
} from '@/helpers/process'
|
|
||||||
import { loading_listener, process_listener, offline_listener } from '@/helpers/events'
|
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { progress_bars_list } from '@/helpers/state.js'
|
import { progress_bars_list } from '@/helpers/state.js'
|
||||||
import { refreshOffline, isOffline } from '@/helpers/utils.js'
|
|
||||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { ChatIcon } from '@/assets/icons'
|
import { ChatIcon } from '@/assets/icons'
|
||||||
|
import { get_many } from '@/helpers/profile.js'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const card = ref(null)
|
const card = ref(null)
|
||||||
@@ -135,38 +130,43 @@ const showCard = ref(false)
|
|||||||
|
|
||||||
const showProfiles = ref(false)
|
const showProfiles = ref(false)
|
||||||
|
|
||||||
const currentProcesses = ref(await getRunningProfiles().catch(handleError))
|
const currentProcesses = ref([])
|
||||||
const selectedProfile = ref(currentProcesses.value[0])
|
const selectedProcess = ref()
|
||||||
|
|
||||||
const offline = ref(await isOffline().catch(handleError))
|
const refresh = async () => {
|
||||||
const refreshInternet = async () => {
|
const processes = await getRunningProcesses().catch(handleError)
|
||||||
offline.value = await refreshOffline().catch(handleError)
|
const profiles = await get_many(processes.map((x) => x.profile_path)).catch(handleError)
|
||||||
|
|
||||||
|
currentProcesses.value = processes.map((x) => ({
|
||||||
|
profile: profiles.find((prof) => x.profile_path === prof.path),
|
||||||
|
...x,
|
||||||
|
}))
|
||||||
|
if (!selectedProcess.value || !currentProcesses.value.includes(selectedProcess.value)) {
|
||||||
|
selectedProcess.value = currentProcesses.value[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await refresh()
|
||||||
|
|
||||||
|
const offline = ref(!navigator.onLine)
|
||||||
|
window.addEventListener('offline', () => {
|
||||||
|
offline.value = true
|
||||||
|
})
|
||||||
|
window.addEventListener('online', () => {
|
||||||
|
offline.value = false
|
||||||
|
})
|
||||||
|
|
||||||
const unlistenProcess = await process_listener(async () => {
|
const unlistenProcess = await process_listener(async () => {
|
||||||
await refresh()
|
await refresh()
|
||||||
})
|
})
|
||||||
|
|
||||||
const unlistenRefresh = await offline_listener(async (b) => {
|
const stop = async (process) => {
|
||||||
offline.value = b
|
|
||||||
await refresh()
|
|
||||||
})
|
|
||||||
|
|
||||||
const refresh = async () => {
|
|
||||||
currentProcesses.value = await getRunningProfiles().catch(handleError)
|
|
||||||
if (!currentProcesses.value.includes(selectedProfile.value)) {
|
|
||||||
selectedProfile.value = currentProcesses.value[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const stop = async (path) => {
|
|
||||||
try {
|
try {
|
||||||
const processes = await getProfileProcesses(path ?? selectedProfile.value.path)
|
await killProcess(process.uuid).catch(handleError)
|
||||||
await killProfile(processes[0])
|
|
||||||
|
|
||||||
mixpanel_track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: currentProcesses.value[0].metadata.loader,
|
loader: process.profile.loader,
|
||||||
game_version: currentProcesses.value[0].metadata.game_version,
|
game_version: process.profile.game_version,
|
||||||
source: 'AppBar',
|
source: 'AppBar',
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -176,7 +176,7 @@ const stop = async (path) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goToTerminal = (path) => {
|
const goToTerminal = (path) => {
|
||||||
router.push(`/instance/${encodeURIComponent(path ?? selectedProfile.value.path)}/logs`)
|
router.push(`/instance/${encodeURIComponent(path ?? selectedProcess.value.profile.path)}/logs`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentLoadingBars = ref([])
|
const currentLoadingBars = ref([])
|
||||||
@@ -188,8 +188,8 @@ const refreshInfo = async () => {
|
|||||||
if (x.bar_type.type === 'java_download') {
|
if (x.bar_type.type === 'java_download') {
|
||||||
x.title = 'Downloading Java ' + x.bar_type.version
|
x.title = 'Downloading Java ' + x.bar_type.version
|
||||||
}
|
}
|
||||||
if (x.bar_type.profile_name) {
|
if (x.bar_type.profile_path) {
|
||||||
x.title = x.bar_type.profile_name
|
x.title = x.bar_type.profile_path
|
||||||
}
|
}
|
||||||
if (x.bar_type.pack_name) {
|
if (x.bar_type.pack_name) {
|
||||||
x.title = x.bar_type.pack_name
|
x.title = x.bar_type.pack_name
|
||||||
@@ -221,8 +221,8 @@ const unlistenLoading = await loading_listener(async () => {
|
|||||||
await refreshInfo()
|
await refreshInfo()
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectProfile = (profile) => {
|
const selectProcess = (process) => {
|
||||||
selectedProfile.value = profile
|
selectedProcess.value = process
|
||||||
showProfiles.value = false
|
showProfiles.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +273,6 @@ onBeforeUnmount(() => {
|
|||||||
window.removeEventListener('click', handleClickOutsideProfile)
|
window.removeEventListener('click', handleClickOutsideProfile)
|
||||||
unlistenProcess()
|
unlistenProcess()
|
||||||
unlistenLoading()
|
unlistenLoading()
|
||||||
unlistenRefresh()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -341,8 +340,12 @@ onBeforeUnmount(() => {
|
|||||||
width: 1.25rem !important;
|
width: 1.25rem !important;
|
||||||
height: 1.25rem !important;
|
height: 1.25rem !important;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
min-width: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
&.stop {
|
&.stop {
|
||||||
--text-color: var(--color-red) !important;
|
color: var(--color-red);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,29 +63,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import { DownloadIcon, HeartIcon, CalendarIcon, CheckIcon, StarIcon } from '@modrinth/assets'
|
||||||
Avatar,
|
import { Avatar, Card, Categories, EnvironmentIndicator, Button } from '@modrinth/ui'
|
||||||
Card,
|
import { formatNumber, formatCategory } from '@modrinth/utils'
|
||||||
Categories,
|
|
||||||
EnvironmentIndicator,
|
|
||||||
Button,
|
|
||||||
DownloadIcon,
|
|
||||||
formatNumber,
|
|
||||||
formatCategory,
|
|
||||||
HeartIcon,
|
|
||||||
CalendarIcon,
|
|
||||||
CheckIcon,
|
|
||||||
StarIcon,
|
|
||||||
} from 'omorphia'
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { add_project_from_version as installMod, list } from '@/helpers/profile.js'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
import { install as packInstall } from '@/helpers/pack.js'
|
|
||||||
import { installVersionDependencies } from '@/helpers/utils.js'
|
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -105,18 +89,6 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
confirmModal: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
modInstallModal: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
incompatibilityWarningModal: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
featured: {
|
featured: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
@@ -134,93 +106,19 @@ const installed = ref(props.installed)
|
|||||||
|
|
||||||
async function install() {
|
async function install() {
|
||||||
installing.value = true
|
installing.value = true
|
||||||
const versions = await useFetch(
|
await installVersion(
|
||||||
`https://api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
props.project.project_id,
|
||||||
'project versions',
|
null,
|
||||||
)
|
props.instance ? props.instance.path : null,
|
||||||
let queuedVersionData
|
'SearchCard',
|
||||||
|
(version) => {
|
||||||
if (!props.instance) {
|
|
||||||
queuedVersionData = versions[0]
|
|
||||||
} else {
|
|
||||||
queuedVersionData = versions.find(
|
|
||||||
(v) =>
|
|
||||||
v.game_versions.includes(props.instance.metadata.game_version) &&
|
|
||||||
(props.project.project_type !== 'mod' ||
|
|
||||||
v.loaders.includes(props.instance.metadata.loader)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.project.project_type === 'modpack') {
|
|
||||||
const packs = Object.values(await list().catch(handleError))
|
|
||||||
if (
|
|
||||||
packs.length === 0 ||
|
|
||||||
!packs
|
|
||||||
.map((value) => value.metadata)
|
|
||||||
.find((pack) => pack.linked_data?.project_id === props.project.project_id)
|
|
||||||
) {
|
|
||||||
await packInstall(
|
|
||||||
props.project.project_id,
|
|
||||||
queuedVersionData.id,
|
|
||||||
props.project.title,
|
|
||||||
props.project.icon_url,
|
|
||||||
).catch(handleError)
|
|
||||||
|
|
||||||
mixpanel_track('PackInstall', {
|
|
||||||
id: props.project.project_id,
|
|
||||||
version_id: queuedVersionData.id,
|
|
||||||
title: props.project.title,
|
|
||||||
source: 'SearchCard',
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
props.confirmModal.show(
|
|
||||||
props.project.project_id,
|
|
||||||
queuedVersionData.id,
|
|
||||||
props.project.title,
|
|
||||||
props.project.icon_url,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (props.instance) {
|
|
||||||
if (!queuedVersionData) {
|
|
||||||
props.incompatibilityWarningModal.show(
|
|
||||||
props.instance,
|
|
||||||
props.project.title,
|
|
||||||
versions,
|
|
||||||
() => (installed.value = true),
|
|
||||||
props.project.project_id,
|
|
||||||
props.project.project_type,
|
|
||||||
)
|
|
||||||
installing.value = false
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
await installMod(props.instance.path, queuedVersionData.id).catch(handleError)
|
|
||||||
await installVersionDependencies(props.instance, queuedVersionData)
|
|
||||||
|
|
||||||
mixpanel_track('ProjectInstall', {
|
|
||||||
loader: props.instance.metadata.loader,
|
|
||||||
game_version: props.instance.metadata.game_version,
|
|
||||||
id: props.project.project_id,
|
|
||||||
project_type: props.project.project_type,
|
|
||||||
version_id: queuedVersionData.id,
|
|
||||||
title: props.project.title,
|
|
||||||
source: 'SearchCard',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
props.modInstallModal.show(
|
|
||||||
props.project.project_id,
|
|
||||||
versions,
|
|
||||||
props.project.title,
|
|
||||||
props.project.project_type,
|
|
||||||
)
|
|
||||||
installing.value = false
|
installing.value = false
|
||||||
return
|
|
||||||
}
|
|
||||||
if (props.instance) installed.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
installing.value = false
|
if (props.instance && version) {
|
||||||
|
installed.value = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
225
apps/app-frontend/src/components/ui/SplashScreen.vue
Normal file
@@ -1,77 +1,42 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Modal, Button } from 'omorphia'
|
import { Modal, Button } from '@modrinth/ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import SearchCard from '@/components/ui/SearchCard.vue'
|
import SearchCard from '@/components/ui/SearchCard.vue'
|
||||||
import { get_categories } from '@/helpers/tags.js'
|
import { get_categories } from '@/helpers/tags.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { install as packInstall } from '@/helpers/pack.js'
|
import { get_version, get_project } from '@/helpers/cache.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
|
||||||
|
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const project = ref(null)
|
const project = ref(null)
|
||||||
const version = ref(null)
|
const version = ref(null)
|
||||||
const categories = ref(null)
|
const categories = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
const modInstallModal = ref(null)
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
async show(event) {
|
async show(event) {
|
||||||
if (event.event === 'InstallVersion') {
|
if (event.event === 'InstallVersion') {
|
||||||
version.value = await useFetch(
|
version.value = await get_version(event.id, 'must_revalidate').catch(handleError)
|
||||||
`https://api.modrinth.com/v2/version/${encodeURIComponent(event.id)}`,
|
project.value = await get_project(version.value.project_id, 'must_revalidate').catch(
|
||||||
'version',
|
handleError,
|
||||||
)
|
|
||||||
project.value = await useFetch(
|
|
||||||
`https://api.modrinth.com/v2/project/${encodeURIComponent(version.value.project_id)}`,
|
|
||||||
'project',
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
project.value = await useFetch(
|
project.value = await get_project(event.id, 'must_revalidate').catch(handleError)
|
||||||
`https://api.modrinth.com/v2/project/${encodeURIComponent(event.id)}`,
|
version.value = await get_version(
|
||||||
'project',
|
project.value.versions[project.value.versions.length - 1],
|
||||||
)
|
'must_revalidate',
|
||||||
version.value = await useFetch(
|
).catch(handleError)
|
||||||
`https://api.modrinth.com/v2/version/${encodeURIComponent(project.value.versions[0])}`,
|
|
||||||
'version',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
categories.value = (await get_categories().catch(handleError)).filter(
|
categories.value = (await get_categories().catch(handleError)).filter(
|
||||||
(cat) => project.value.categories.includes(cat.name) && cat.project_type === 'mod',
|
(cat) => project.value.categories.includes(cat.name) && cat.project_type === 'mod',
|
||||||
)
|
)
|
||||||
confirmModal.value.show()
|
confirmModal.value.show()
|
||||||
categories.value = (await get_categories().catch(handleError)).filter(
|
|
||||||
(cat) => project.value.categories.includes(cat.name) && cat.project_type === 'mod',
|
|
||||||
)
|
|
||||||
confirmModal.value.show()
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
async function install() {
|
async function install() {
|
||||||
confirmModal.value.hide()
|
confirmModal.value.hide()
|
||||||
if (project.value.project_type === 'modpack') {
|
await installVersion(project.value.id, version.value.id, null, 'URLConfirmModal')
|
||||||
await packInstall(
|
|
||||||
project.value.id,
|
|
||||||
version.value.id,
|
|
||||||
project.value.title,
|
|
||||||
project.value.icon_url,
|
|
||||||
).catch(handleError)
|
|
||||||
|
|
||||||
mixpanel.track('PackInstall', {
|
|
||||||
id: project.value.id,
|
|
||||||
version_id: version.value.id,
|
|
||||||
title: project.value.title,
|
|
||||||
source: 'ProjectPage',
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
modInstallModal.value.show(
|
|
||||||
project.value.id,
|
|
||||||
[version.value],
|
|
||||||
project.value.title,
|
|
||||||
project.value.project_type,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -96,7 +61,6 @@ async function install() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
<ModInstallModal ref="modInstallModal" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
ref="incompatibleModal"
|
ref="incompatibleModal"
|
||||||
header="Incompatibility warning"
|
header="Incompatibility warning"
|
||||||
:noblur="!themeStore.advancedRendering"
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
:on-hide="onInstall"
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>
|
<p>
|
||||||
@@ -12,13 +13,11 @@
|
|||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<tr class="header">
|
<tr class="header">
|
||||||
<th>{{ instance?.metadata.name }}</th>
|
<th>{{ instance?.name }}</th>
|
||||||
<th>{{ projectTitle }}</th>
|
<th>{{ project.title }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="content">
|
<tr class="content">
|
||||||
<td class="data">
|
<td class="data">{{ instance?.loader }} {{ instance?.game_version }}</td>
|
||||||
{{ instance?.metadata.loader }} {{ instance?.metadata.game_version }}
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<DropdownSelect
|
<DropdownSelect
|
||||||
v-if="versions?.length > 1"
|
v-if="versions?.length > 1"
|
||||||
@@ -56,7 +55,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Button, Modal, XIcon, DownloadIcon, DropdownSelect, formatCategory } from 'omorphia'
|
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
||||||
|
import { Button, Modal, DropdownSelect } from '@modrinth/ui'
|
||||||
|
import { formatCategory } from '@modrinth/utils'
|
||||||
import { add_project_from_version as installMod } from '@/helpers/profile'
|
import { add_project_from_version as installMod } from '@/helpers/profile'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { handleError, useTheming } from '@/store/state.js'
|
import { handleError, useTheming } from '@/store/state.js'
|
||||||
@@ -66,34 +67,25 @@ const themeStore = useTheming()
|
|||||||
|
|
||||||
const instance = ref(null)
|
const instance = ref(null)
|
||||||
const project = ref(null)
|
const project = ref(null)
|
||||||
const projectType = ref(null)
|
|
||||||
const projectTitle = ref(null)
|
|
||||||
const versions = ref(null)
|
const versions = ref(null)
|
||||||
const selectedVersion = ref(null)
|
const selectedVersion = ref(null)
|
||||||
const incompatibleModal = ref(null)
|
const incompatibleModal = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
|
|
||||||
let markInstalled = () => {}
|
let onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: (
|
show: (instanceVal, projectVal, projectVersions, callback) => {
|
||||||
instanceVal,
|
|
||||||
projectTitleVal,
|
|
||||||
selectedVersions,
|
|
||||||
extMarkInstalled,
|
|
||||||
projectIdVal,
|
|
||||||
projectTypeVal,
|
|
||||||
) => {
|
|
||||||
instance.value = instanceVal
|
instance.value = instanceVal
|
||||||
projectTitle.value = projectTitleVal
|
versions.value = projectVersions
|
||||||
versions.value = selectedVersions
|
selectedVersion.value = projectVersions[0]
|
||||||
selectedVersion.value = selectedVersions[0]
|
|
||||||
|
|
||||||
project.value = projectIdVal
|
project.value = projectVal
|
||||||
projectType.value = projectTypeVal
|
|
||||||
|
onInstall.value = callback
|
||||||
|
installing.value = false
|
||||||
|
|
||||||
incompatibleModal.value.show()
|
incompatibleModal.value.show()
|
||||||
markInstalled = extMarkInstalled
|
|
||||||
|
|
||||||
mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
||||||
},
|
},
|
||||||
@@ -103,16 +95,16 @@ const install = async () => {
|
|||||||
installing.value = true
|
installing.value = true
|
||||||
await installMod(instance.value.path, selectedVersion.value.id).catch(handleError)
|
await installMod(instance.value.path, selectedVersion.value.id).catch(handleError)
|
||||||
installing.value = false
|
installing.value = false
|
||||||
markInstalled()
|
onInstall.value(selectedVersion.value.id)
|
||||||
incompatibleModal.value.hide()
|
incompatibleModal.value.hide()
|
||||||
|
|
||||||
mixpanel_track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.value.metadata.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.metadata.game_version,
|
game_version: instance.value.game_version,
|
||||||
id: project.value,
|
id: project.value,
|
||||||
version_id: selectedVersion.value.id,
|
version_id: selectedVersion.value.id,
|
||||||
project_type: projectType.value,
|
project_type: project.value.project_type,
|
||||||
title: projectTitle.value,
|
title: project.value.title,
|
||||||
source: 'ProjectIncompatibilityWarningModal',
|
source: 'ProjectIncompatibilityWarningModal',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Button, Modal, XIcon, DownloadIcon } from 'omorphia'
|
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
||||||
|
import { Button, Modal } from '@modrinth/ui'
|
||||||
import { install as pack_install } from '@/helpers/pack'
|
import { install as pack_install } from '@/helpers/pack'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
@@ -8,47 +9,55 @@ import { handleError } from '@/store/state.js'
|
|||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const version = ref('')
|
const versionId = ref()
|
||||||
const title = ref('')
|
const project = ref()
|
||||||
const projectId = ref('')
|
|
||||||
const icon = ref('')
|
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
|
|
||||||
|
let onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: (projectIdVal, versionId, projectTitle, projectIcon) => {
|
show: (projectVal, versionIdVal, callback) => {
|
||||||
projectId.value = projectIdVal
|
project.value = projectVal
|
||||||
version.value = versionId
|
versionId.value = versionIdVal
|
||||||
title.value = projectTitle
|
|
||||||
icon.value = projectIcon
|
|
||||||
installing.value = false
|
installing.value = false
|
||||||
confirmModal.value.show()
|
confirmModal.value.show()
|
||||||
|
|
||||||
|
onInstall.value = callback
|
||||||
|
|
||||||
mixpanel_track('PackInstallStart')
|
mixpanel_track('PackInstallStart')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
async function install() {
|
async function install() {
|
||||||
installing.value = true
|
installing.value = true
|
||||||
console.log(`Installing ${projectId.value} ${version.value} ${title.value} ${icon.value}`)
|
|
||||||
confirmModal.value.hide()
|
confirmModal.value.hide()
|
||||||
|
|
||||||
await pack_install(
|
await pack_install(
|
||||||
projectId.value,
|
project.value.id,
|
||||||
version.value,
|
versionId.value,
|
||||||
title.value,
|
project.value.title,
|
||||||
icon.value ? icon.value : null,
|
project.value.icon_url,
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
mixpanel_track('PackInstall', {
|
mixpanel_track('PackInstall', {
|
||||||
id: projectId.value,
|
id: project.value.id,
|
||||||
version_id: version.value,
|
version_id: versionId.value,
|
||||||
title: title.value,
|
title: project.value.title,
|
||||||
source: 'ConfirmModal',
|
source: 'ConfirmModal',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onInstall.value(versionId.value)
|
||||||
|
installing.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal ref="confirmModal" header="Are you sure?" :noblur="!themeStore.advancedRendering">
|
<Modal
|
||||||
|
ref="confirmModal"
|
||||||
|
header="Are you sure?"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
:on-hide="onInstall"
|
||||||
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>You already have this modpack installed. Are you sure you want to install it again?</p>
|
<p>You already have this modpack installed. Are you sure you want to install it again?</p>
|
||||||
<div class="input-group push-right">
|
<div class="input-group push-right">
|
||||||
@@ -1,26 +1,23 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import {
|
||||||
Avatar,
|
|
||||||
Modal,
|
|
||||||
Button,
|
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
Card,
|
|
||||||
UploadIcon,
|
UploadIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
RightArrowIcon,
|
RightArrowIcon,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
} from 'omorphia'
|
} from '@modrinth/assets'
|
||||||
|
import { Avatar, Modal, Button, Card } from '@modrinth/ui'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import {
|
import {
|
||||||
add_project_from_version as installMod,
|
add_project_from_version as installMod,
|
||||||
check_installed,
|
check_installed,
|
||||||
get,
|
get,
|
||||||
list,
|
list,
|
||||||
|
create,
|
||||||
} from '@/helpers/profile'
|
} from '@/helpers/profile'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { create } from '@/helpers/profile'
|
import { installVersionDependencies } from '@/store/install.js'
|
||||||
import { installVersionDependencies } from '@/helpers/utils'
|
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { useTheming } from '@/store/theme.js'
|
import { useTheming } from '@/store/theme.js'
|
||||||
@@ -30,13 +27,12 @@ import { tauri } from '@tauri-apps/api'
|
|||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const versions = ref([])
|
const versions = ref()
|
||||||
const project = ref('')
|
const project = ref()
|
||||||
const projectTitle = ref('')
|
|
||||||
const projectType = ref('')
|
|
||||||
|
|
||||||
const installModal = ref(null)
|
const installModal = ref()
|
||||||
const searchFilter = ref('')
|
const searchFilter = ref('')
|
||||||
|
|
||||||
const showCreation = ref(false)
|
const showCreation = ref(false)
|
||||||
const icon = ref(null)
|
const icon = ref(null)
|
||||||
const name = ref(null)
|
const name = ref(null)
|
||||||
@@ -45,33 +41,65 @@ const loader = ref(null)
|
|||||||
const gameVersion = ref(null)
|
const gameVersion = ref(null)
|
||||||
const creatingInstance = ref(false)
|
const creatingInstance = ref(false)
|
||||||
|
|
||||||
defineExpose({
|
const profiles = ref([])
|
||||||
show: async (projectId, selectedVersions, title, type) => {
|
|
||||||
project.value = projectId
|
|
||||||
versions.value = selectedVersions
|
|
||||||
projectTitle.value = title
|
|
||||||
projectType.value = type
|
|
||||||
|
|
||||||
installModal.value.show()
|
const shownProfiles = computed(() =>
|
||||||
|
profiles.value
|
||||||
|
.filter((profile) => {
|
||||||
|
return profile.name.toLowerCase().includes(searchFilter.value.toLowerCase())
|
||||||
|
})
|
||||||
|
.filter((profile) => {
|
||||||
|
let loaders = versions.value.flatMap((v) => v.loaders)
|
||||||
|
|
||||||
|
return (
|
||||||
|
versions.value.flatMap((v) => v.game_versions).includes(profile.game_version) &&
|
||||||
|
(project.value.project_type === 'mod'
|
||||||
|
? loaders.includes(profile.loader) || loaders.includes('minecraft')
|
||||||
|
: true)
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
let onInstall = ref(() => {})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show: async (projectVal, versionsVal, callback) => {
|
||||||
|
project.value = projectVal
|
||||||
|
versions.value = versionsVal
|
||||||
searchFilter.value = ''
|
searchFilter.value = ''
|
||||||
|
|
||||||
profiles.value = await getData()
|
showCreation.value = false
|
||||||
|
name.value = null
|
||||||
|
icon.value = null
|
||||||
|
display_icon.value = null
|
||||||
|
gameVersion.value = null
|
||||||
|
loader.value = null
|
||||||
|
|
||||||
|
onInstall.value = callback
|
||||||
|
|
||||||
|
const profilesVal = await list().catch(handleError)
|
||||||
|
for (let profile of profilesVal) {
|
||||||
|
profile.installing = false
|
||||||
|
profile.installedMod = await check_installed(profile.path, project.value.id).catch(
|
||||||
|
handleError,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
profiles.value = profilesVal
|
||||||
|
|
||||||
|
installModal.value.show()
|
||||||
|
|
||||||
mixpanel_track('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
mixpanel_track('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const profiles = ref([])
|
|
||||||
|
|
||||||
async function install(instance) {
|
async function install(instance) {
|
||||||
instance.installing = true
|
instance.installing = true
|
||||||
const version = versions.value.find((v) => {
|
const version = versions.value.find((v) => {
|
||||||
return (
|
return (
|
||||||
v.game_versions.includes(instance.metadata.game_version) &&
|
v.game_versions.includes(instance.game_version) &&
|
||||||
(v.loaders.includes(instance.metadata.loader) ||
|
(project.value.project_type === 'mod'
|
||||||
v.loaders.includes('minecraft') ||
|
? v.loaders.includes(instance.loader) || v.loaders.includes('minecraft')
|
||||||
v.loaders.includes('iris') ||
|
: true)
|
||||||
v.loaders.includes('optifine'))
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -88,45 +116,18 @@ async function install(instance) {
|
|||||||
instance.installing = false
|
instance.installing = false
|
||||||
|
|
||||||
mixpanel_track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.metadata.loader,
|
loader: instance.loader,
|
||||||
game_version: instance.metadata.game_version,
|
game_version: instance.game_version,
|
||||||
id: project.value,
|
id: project.value.id,
|
||||||
version_id: version.id,
|
version_id: version.id,
|
||||||
project_type: projectType.value,
|
project_type: project.value.project_type,
|
||||||
title: projectTitle.value,
|
title: project.value.title,
|
||||||
source: 'ProjectInstallModal',
|
source: 'ProjectInstallModal',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onInstall.value(version.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getData() {
|
|
||||||
const projects = await list(true).then(Object.values).catch(handleError)
|
|
||||||
|
|
||||||
const filtered = projects
|
|
||||||
.filter((profile) => {
|
|
||||||
return profile.metadata.name.toLowerCase().includes(searchFilter.value.toLowerCase())
|
|
||||||
})
|
|
||||||
.filter((profile) => {
|
|
||||||
return (
|
|
||||||
versions.value.flatMap((v) => v.game_versions).includes(profile.metadata.game_version) &&
|
|
||||||
versions.value
|
|
||||||
.flatMap((v) => v.loaders)
|
|
||||||
.some(
|
|
||||||
(value) =>
|
|
||||||
value === profile.metadata.loader ||
|
|
||||||
['minecraft', 'iris', 'optifine'].includes(value),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
for (let profile of filtered) {
|
|
||||||
profile.installing = false
|
|
||||||
profile.installedMod = await check_installed(profile.path, project.value).catch(handleError)
|
|
||||||
}
|
|
||||||
|
|
||||||
return filtered
|
|
||||||
}
|
|
||||||
|
|
||||||
const alreadySentCreation = ref(false)
|
|
||||||
const toggleCreation = () => {
|
const toggleCreation = () => {
|
||||||
showCreation.value = !showCreation.value
|
showCreation.value = !showCreation.value
|
||||||
name.value = null
|
name.value = null
|
||||||
@@ -135,8 +136,7 @@ const toggleCreation = () => {
|
|||||||
gameVersion.value = null
|
gameVersion.value = null
|
||||||
loader.value = null
|
loader.value = null
|
||||||
|
|
||||||
if (!alreadySentCreation.value) {
|
if (showCreation.value) {
|
||||||
alreadySentCreation.value = false
|
|
||||||
mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,18 +200,16 @@ const createInstance = async () => {
|
|||||||
game_version: versions.value[0].game_versions[0],
|
game_version: versions.value[0].game_versions[0],
|
||||||
id: project.value,
|
id: project.value,
|
||||||
version_id: versions.value[0].id,
|
version_id: versions.value[0].id,
|
||||||
project_type: projectType.value,
|
project_type: project.value.project_type,
|
||||||
title: projectTitle.value,
|
title: project.value.title,
|
||||||
source: 'ProjectInstallModal',
|
source: 'ProjectInstallModal',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onInstall.value(versions.value[0].id)
|
||||||
|
|
||||||
if (installModal.value) installModal.value.hide()
|
if (installModal.value) installModal.value.hide()
|
||||||
creatingInstance.value = false
|
creatingInstance.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const check_valid = computed(() => {
|
|
||||||
return name.value
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -219,6 +217,7 @@ const check_valid = computed(() => {
|
|||||||
ref="installModal"
|
ref="installModal"
|
||||||
header="Install project to instance"
|
header="Install project to instance"
|
||||||
:noblur="!themeStore.advancedRendering"
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
:on-hide="onInstall"
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input
|
<input
|
||||||
@@ -229,34 +228,27 @@ const check_valid = computed(() => {
|
|||||||
placeholder="Search for an instance"
|
placeholder="Search for an instance"
|
||||||
/>
|
/>
|
||||||
<div class="profiles" :class="{ 'hide-creation': !showCreation }">
|
<div class="profiles" :class="{ 'hide-creation': !showCreation }">
|
||||||
<div v-for="profile in profiles" :key="profile.metadata.name" class="option">
|
<div v-for="profile in shownProfiles" :key="profile.name" class="option">
|
||||||
<Button
|
<router-link
|
||||||
color="raised"
|
class="btn btn-transparent profile-button"
|
||||||
class="profile-button"
|
:to="`/instance/${encodeURIComponent(profile.path)}`"
|
||||||
@click="$router.push(`/instance/${encodeURIComponent(profile.path)}`)"
|
@click="installModal.hide()"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
:src="
|
:src="profile.icon_path ? tauri.convertFileSrc(profile.icon_path) : null"
|
||||||
!profile.metadata.icon ||
|
|
||||||
(profile.metadata.icon && profile.metadata.icon.startsWith('http'))
|
|
||||||
? profile.metadata.icon
|
|
||||||
: tauri.convertFileSrc(profile.metadata?.icon)
|
|
||||||
"
|
|
||||||
class="profile-image"
|
class="profile-image"
|
||||||
/>
|
/>
|
||||||
{{ profile.metadata.name }}
|
{{ profile.name }}
|
||||||
</Button>
|
</router-link>
|
||||||
<div
|
<div
|
||||||
v-tooltip="
|
v-tooltip="
|
||||||
profile.metadata.linked_data?.locked && !profile.installedMod
|
profile.linked_data?.locked && !profile.installedMod
|
||||||
? 'Unpair or unlock an instance to add mods.'
|
? 'Unpair or unlock an instance to add mods.'
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
:disabled="
|
:disabled="profile.installedMod || profile.installing || profile.linked_data?.locked"
|
||||||
profile.installedMod || profile.installing || profile.metadata.linked_data?.locked
|
|
||||||
"
|
|
||||||
@click="install(profile)"
|
@click="install(profile)"
|
||||||
>
|
>
|
||||||
<DownloadIcon v-if="!profile.installedMod && !profile.installing" />
|
<DownloadIcon v-if="!profile.installedMod && !profile.installing" />
|
||||||
@@ -266,7 +258,7 @@ const check_valid = computed(() => {
|
|||||||
? 'Installing...'
|
? 'Installing...'
|
||||||
: profile.installedMod
|
: profile.installedMod
|
||||||
? 'Installed'
|
? 'Installed'
|
||||||
: profile.metadata.linked_data && profile.metadata.linked_data.locked
|
: profile.linked_data && profile.linked_data.locked
|
||||||
? 'Paired'
|
? 'Paired'
|
||||||
: 'Install'
|
: 'Install'
|
||||||
}}
|
}}
|
||||||
@@ -297,7 +289,7 @@ const check_valid = computed(() => {
|
|||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
class="creation-input"
|
class="creation-input"
|
||||||
/>
|
/>
|
||||||
<Button :disabled="creatingInstance === true || !check_valid" @click="createInstance()">
|
<Button :disabled="creatingInstance === true || !name" @click="createInstance()">
|
||||||
<RightArrowIcon />
|
<RightArrowIcon />
|
||||||
{{ creatingInstance ? 'Creating...' : 'Create' }}
|
{{ creatingInstance ? 'Creating...' : 'Create' }}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -0,0 +1,367 @@
|
|||||||
|
<script setup>
|
||||||
|
import { UserIcon, LockIcon, MailIcon } from '@modrinth/assets'
|
||||||
|
import { Button, Card, Checkbox, Modal } from '@modrinth/ui'
|
||||||
|
import {
|
||||||
|
DiscordIcon,
|
||||||
|
GithubIcon,
|
||||||
|
MicrosoftIcon,
|
||||||
|
GoogleIcon,
|
||||||
|
SteamIcon,
|
||||||
|
GitLabIcon,
|
||||||
|
} from '@/assets/external'
|
||||||
|
import { login, login_2fa, create_account, login_pass } from '@/helpers/mr_auth.js'
|
||||||
|
import { handleError, useNotifications } from '@/store/state.js'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { handleSevereError } from '@/store/error.js'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
callback: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const modal = ref()
|
||||||
|
const turnstileToken = ref()
|
||||||
|
const widgetId = ref()
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show: () => {
|
||||||
|
modal.value.show()
|
||||||
|
|
||||||
|
if (window.turnstile === null || !window.turnstile) {
|
||||||
|
const script = document.createElement('script')
|
||||||
|
script.src =
|
||||||
|
'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback'
|
||||||
|
script.async = true
|
||||||
|
script.defer = true
|
||||||
|
document.head.appendChild(script)
|
||||||
|
|
||||||
|
window.onloadTurnstileCallback = loadWidget
|
||||||
|
} else {
|
||||||
|
loadWidget()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function loadWidget() {
|
||||||
|
widgetId.value = window.turnstile.render('#turnstile-container', {
|
||||||
|
sitekey: '0x4AAAAAAAW3guHM6Eunbgwu',
|
||||||
|
callback: (token) => (turnstileToken.value = token),
|
||||||
|
expiredCallback: () => (turnstileToken.value = null),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeWidget() {
|
||||||
|
if (widgetId.value) {
|
||||||
|
window.turnstile.remove(widgetId.value)
|
||||||
|
widgetId.value = null
|
||||||
|
turnstileToken.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loggingIn = ref(true)
|
||||||
|
const twoFactorFlow = ref(null)
|
||||||
|
const twoFactorCode = ref('')
|
||||||
|
|
||||||
|
const email = ref('')
|
||||||
|
const username = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
const confirmPassword = ref('')
|
||||||
|
const subscribe = ref(true)
|
||||||
|
|
||||||
|
async function signInOauth(provider) {
|
||||||
|
const creds = await login(provider).catch(handleSevereError)
|
||||||
|
|
||||||
|
if (creds && creds.type === 'two_factor_required') {
|
||||||
|
twoFactorFlow.value = creds.flow
|
||||||
|
} else if (creds && creds.session) {
|
||||||
|
props.callback()
|
||||||
|
modal.value.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signIn2fa() {
|
||||||
|
const creds = await login_2fa(twoFactorCode.value, twoFactorFlow.value).catch(handleError)
|
||||||
|
|
||||||
|
if (creds && creds.session) {
|
||||||
|
props.callback()
|
||||||
|
modal.value.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signIn() {
|
||||||
|
const creds = await login_pass(username.value, password.value, turnstileToken.value).catch(
|
||||||
|
handleError,
|
||||||
|
)
|
||||||
|
window.turnstile.reset(widgetId.value)
|
||||||
|
|
||||||
|
if (creds && creds.type === 'two_factor_required') {
|
||||||
|
twoFactorFlow.value = creds.flow
|
||||||
|
} else if (creds && creds.session) {
|
||||||
|
props.callback()
|
||||||
|
modal.value.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createAccount() {
|
||||||
|
if (password.value !== confirmPassword.value) {
|
||||||
|
const notifs = useNotifications()
|
||||||
|
notifs.addNotification({
|
||||||
|
title: 'An error occurred',
|
||||||
|
text: 'Passwords do not match!',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const creds = await create_account(
|
||||||
|
username.value,
|
||||||
|
email.value,
|
||||||
|
password.value,
|
||||||
|
turnstileToken.value,
|
||||||
|
subscribe.value,
|
||||||
|
).catch(handleError)
|
||||||
|
window.turnstile.reset(widgetId.value)
|
||||||
|
|
||||||
|
if (creds && creds.session) {
|
||||||
|
props.callback()
|
||||||
|
modal.value.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal ref="modal" :on-hide="removeWidget">
|
||||||
|
<Card>
|
||||||
|
<template v-if="twoFactorFlow">
|
||||||
|
<h1>Enter two-factor code</h1>
|
||||||
|
<p>Please enter a two-factor code to proceed.</p>
|
||||||
|
<input v-model="twoFactorCode" maxlength="11" type="text" placeholder="Enter code..." />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<h1 v-if="loggingIn">Login to Modrinth</h1>
|
||||||
|
<h1 v-else>Create an account</h1>
|
||||||
|
<div class="button-grid">
|
||||||
|
<Button class="discord" large @click="signInOauth('discord')">
|
||||||
|
<DiscordIcon />
|
||||||
|
Discord
|
||||||
|
</Button>
|
||||||
|
<Button class="github" large @click="signInOauth('github')">
|
||||||
|
<GithubIcon />
|
||||||
|
Github
|
||||||
|
</Button>
|
||||||
|
<Button class="white" large @click="signInOauth('microsoft')">
|
||||||
|
<MicrosoftIcon />
|
||||||
|
Microsoft
|
||||||
|
</Button>
|
||||||
|
<Button class="google" large @click="signInOauth('google')">
|
||||||
|
<GoogleIcon />
|
||||||
|
Google
|
||||||
|
</Button>
|
||||||
|
<Button class="white" large @click="signInOauth('steam')">
|
||||||
|
<SteamIcon />
|
||||||
|
Steam
|
||||||
|
</Button>
|
||||||
|
<Button class="gitlab" large @click="signInOauth('gitlab')">
|
||||||
|
<GitLabIcon />
|
||||||
|
GitLab
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div class="divider">
|
||||||
|
<hr />
|
||||||
|
<p>Or</p>
|
||||||
|
</div>
|
||||||
|
<div v-if="!loggingIn" class="iconified-input username">
|
||||||
|
<MailIcon />
|
||||||
|
<input v-model="email" type="text" placeholder="Email" />
|
||||||
|
</div>
|
||||||
|
<div class="iconified-input username">
|
||||||
|
<UserIcon />
|
||||||
|
<input
|
||||||
|
v-model="username"
|
||||||
|
type="text"
|
||||||
|
:placeholder="loggingIn ? 'Email or username' : 'Username'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="iconified-input" :class="{ username: !loggingIn }">
|
||||||
|
<LockIcon />
|
||||||
|
<input v-model="password" type="password" placeholder="Password" />
|
||||||
|
</div>
|
||||||
|
<div v-if="!loggingIn" class="iconified-input username">
|
||||||
|
<LockIcon />
|
||||||
|
<input v-model="confirmPassword" type="password" placeholder="Confirm password" />
|
||||||
|
</div>
|
||||||
|
<div class="turnstile">
|
||||||
|
<div id="turnstile-container"></div>
|
||||||
|
<div id="turnstile-container-2"></div>
|
||||||
|
</div>
|
||||||
|
<Checkbox
|
||||||
|
v-if="!loggingIn"
|
||||||
|
v-model="subscribe"
|
||||||
|
class="subscribe-btn"
|
||||||
|
label="Subscribe to updates about Modrinth"
|
||||||
|
/>
|
||||||
|
<div class="link-row">
|
||||||
|
<a v-if="loggingIn" class="button-base" @click="loggingIn = false"> Create account </a>
|
||||||
|
<a v-else class="button-base" @click="loggingIn = true">Sign in</a>
|
||||||
|
<a class="button-base" href="https://modrinth.com/auth/reset-password">
|
||||||
|
Forgot password?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="button-row">
|
||||||
|
<Button class="transparent" large>Close</Button>
|
||||||
|
<Button v-if="twoFactorCode" color="primary" large @click="signIn2fa"> Login </Button>
|
||||||
|
<Button
|
||||||
|
v-else-if="loggingIn"
|
||||||
|
color="primary"
|
||||||
|
large
|
||||||
|
@click="signIn"
|
||||||
|
:disabled="!turnstileToken"
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
<Button v-else color="primary" large @click="createAccount" :disabled="!turnstileToken">
|
||||||
|
Create account
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
:deep(.modal-container) {
|
||||||
|
.modal-body {
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: var(--gap-md);
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discord {
|
||||||
|
background-color: #5865f2;
|
||||||
|
color: var(--color-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.github {
|
||||||
|
background-color: #8740f1;
|
||||||
|
color: var(--color-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.white {
|
||||||
|
background-color: var(--color-contrast);
|
||||||
|
color: var(--color-accent-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.google {
|
||||||
|
background-color: #4285f4;
|
||||||
|
color: var(--color-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gitlab {
|
||||||
|
background-color: #fc6d26;
|
||||||
|
color: var(--color-contrast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: var(--gap-md) 0;
|
||||||
|
|
||||||
|
p {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background-color: var(--color-raised-bg);
|
||||||
|
padding: 0 1rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
border-top: 2px solid var(--color-button-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconified-input {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
flex-basis: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
margin-bottom: var(--gap-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: var(--gap-md) 0;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-blue);
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
flex-basis: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transparent {
|
||||||
|
padding: var(--gap-md) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.checkbox) {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.turnstile {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: 2px solid var(--color-button-bg);
|
||||||
|
height: 66px;
|
||||||
|
margin-top: var(--gap-md);
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
margin: -1px;
|
||||||
|
min-width: calc(100% + 2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -45,11 +45,3 @@ export async function remove_user(user) {
|
|||||||
export async function users() {
|
export async function users() {
|
||||||
return await invoke('plugin:auth|auth_users')
|
return await invoke('plugin:auth|auth_users')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a user by UUID
|
|
||||||
// Prefer to use refresh() instead of this because it will refresh the credentials
|
|
||||||
// user is UUID
|
|
||||||
// Returns Credentials (of user)
|
|
||||||
export async function get_user(user) {
|
|
||||||
return await invoke('plugin:auth|auth_get_user', { user })
|
|
||||||
}
|
|
||||||
53
apps/app-frontend/src/helpers/cache.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
|
export async function get_project(id, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_project', { id, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_project_many(ids, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_project_many', { ids, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_version(id, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_version', { id, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_version_many(ids, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_version_many', { ids, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_user(id, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_user', { id, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_user_many(ids, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_user_many', { ids, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_team(id, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_team', { id, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_team_many(ids, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_team_many', { ids, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_organization(id, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_organization', { id, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_organization_many(ids, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_organization_many', { ids, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_search_results(id, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_search_results', { id, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_search_results_many(ids, cacheBehaviour) {
|
||||||
|
return await invoke('plugin:cache|get_search_results_many', { ids, cacheBehaviour })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function purge_cache_types(cacheTypes) {
|
||||||
|
return await invoke('plugin:cache|purge_cache_types', { cacheTypes })
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
// event.payload is the payload object
|
// event.payload is the payload object
|
||||||
console.log(event)
|
console.log(event)
|
||||||
})
|
})
|
||||||
|
|
||||||
Putting that in a script will print any emitted signal from rust
|
Putting that in a script will print any emitted signal from rust
|
||||||
*/
|
*/
|
||||||
import { listen } from '@tauri-apps/api/event'
|
import { listen } from '@tauri-apps/api/event'
|
||||||
@@ -93,15 +93,3 @@ export async function command_listener(callback) {
|
|||||||
export async function warning_listener(callback) {
|
export async function warning_listener(callback) {
|
||||||
return await listen('warning', (event) => callback(event.payload))
|
return await listen('warning', (event) => callback(event.payload))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Payload for the 'offline' event
|
|
||||||
/*
|
|
||||||
OfflinePayload {
|
|
||||||
offline: bool, true or false
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
export async function offline_listener(callback) {
|
|
||||||
return await listen('offline', (event) => {
|
|
||||||
return callback(event.payload)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,14 @@ JavaVersion {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export async function get_java_versions() {
|
||||||
|
return await invoke('plugin:jre|get_java_versions')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function set_java_version(javaVersion) {
|
||||||
|
return await invoke('plugin:jre|set_java_version', { javaVersion })
|
||||||
|
}
|
||||||
|
|
||||||
// Finds all the installation of Java 7, if it exists
|
// Finds all the installation of Java 7, if it exists
|
||||||
// Returns [JavaVersion]
|
// Returns [JavaVersion]
|
||||||
export async function find_filtered_jres(version) {
|
export async function find_filtered_jres(version) {
|
||||||
13
apps/app-frontend/src/helpers/metadata.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
|
/// Gets the game versions from daedalus
|
||||||
|
// Returns a VersionManifest
|
||||||
|
export async function get_game_versions() {
|
||||||
|
return await invoke('plugin:metadata|metadata_get_game_versions')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the given loader versions from daedalus
|
||||||
|
// Returns Manifest
|
||||||
|
export async function get_loader_versions(loader) {
|
||||||
|
return await invoke('plugin:metadata|metadata_get_loader_versions', { loader })
|
||||||
|
}
|
||||||