Compare commits
847 Commits
plus-theme
...
shared-ins
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96782b52cd | ||
|
|
aae00e1312 | ||
|
|
24e90f0a54 | ||
|
|
f5208a85b0 | ||
|
|
8db4b3f83b | ||
|
|
33ad04d036 | ||
|
|
4bcdb3f495 | ||
|
|
70979172b0 | ||
|
|
493b9a3975 | ||
|
|
5a21a67d46 | ||
|
|
ff72c906ba | ||
|
|
907b1f67ed | ||
|
|
72cbe7f905 | ||
|
|
deb16aa7ab | ||
|
|
d0efa44c9e | ||
|
|
d321843c02 | ||
|
|
2b44b145cb | ||
|
|
9f5606889e | ||
|
|
417f2a8b91 | ||
|
|
d3a7bf967e | ||
|
|
db145cd8ad | ||
|
|
7d614f7ac5 | ||
|
|
c7a2a3e29b | ||
|
|
3b966c03ee | ||
|
|
66d943d391 | ||
|
|
f5f876e458 | ||
|
|
27c3439120 | ||
|
|
f212d04261 | ||
|
|
06f01aa85c | ||
|
|
5f48dc08a9 | ||
|
|
e81e056758 | ||
|
|
2d95ff0830 | ||
|
|
81d921d625 | ||
|
|
e988513ed7 | ||
|
|
783d4f82d9 | ||
|
|
b5011f458f | ||
|
|
5d0e4366d2 | ||
|
|
8643dc02dd | ||
|
|
7c4dcb2817 | ||
|
|
6b64fdafcb | ||
|
|
185dd47668 | ||
|
|
f165665a35 | ||
|
|
ad38749f98 | ||
|
|
7825dd64ca | ||
|
|
f6af620643 | ||
|
|
b5aeef7ebf | ||
|
|
f5a201dd94 | ||
|
|
72dab12033 | ||
|
|
ce4b4ba41d | ||
|
|
945206153d | ||
|
|
9f977d082b | ||
|
|
4a2ec0c40c | ||
|
|
70bf61a645 | ||
|
|
674f4b1095 | ||
|
|
3304070034 | ||
|
|
19ca5f08c6 | ||
|
|
5605910ac8 | ||
|
|
b1eda435a5 | ||
|
|
e88ca8430e | ||
|
|
d39db02a73 | ||
|
|
86a64ca929 | ||
|
|
5ea0f7d4c7 | ||
|
|
b3fa2fa6d2 | ||
|
|
999dc640bc | ||
|
|
9be9658ffb | ||
|
|
595d5362f6 | ||
|
|
f212fcf892 | ||
|
|
8c1c5572c0 | ||
|
|
31d151638a | ||
|
|
fb6b41630c | ||
|
|
6ab806cbde | ||
|
|
c0267f7746 | ||
|
|
a54b6dc7b9 | ||
|
|
9ec43ebe70 | ||
|
|
486cd68bf7 | ||
|
|
98c050e7e9 | ||
|
|
25fcee984b | ||
|
|
86922c4547 | ||
|
|
7bbdfd25cd | ||
|
|
b8ad22a6fb | ||
|
|
8dd955563e | ||
|
|
663ab83b08 | ||
|
|
c143929b69 | ||
|
|
1b73d248b3 | ||
|
|
cc22a92daf | ||
|
|
39f0408929 | ||
|
|
e66f46a464 | ||
|
|
9243296197 | ||
|
|
26ce83f8f1 | ||
|
|
907ef38189 | ||
|
|
a7d4001b00 | ||
|
|
e3a3379615 | ||
|
|
356a06e694 | ||
|
|
fce516a76f | ||
|
|
42ade0fbd1 | ||
|
|
ba07f5dad4 | ||
|
|
cc89e0f3f1 | ||
|
|
0e14d3f9c1 | ||
|
|
ff7975773e | ||
|
|
6716e2277d | ||
|
|
f986dc5d11 | ||
|
|
570a4096f9 | ||
|
|
c88bfbb5f0 | ||
|
|
d302795512 | ||
|
|
115acce80c | ||
|
|
a8731b0ca2 | ||
|
|
fd596bf418 | ||
|
|
ef7cfffeb6 | ||
|
|
ac9bcabd9c | ||
|
|
5bb961f16b | ||
|
|
a46677832b | ||
|
|
624abf0df4 | ||
|
|
e81a4ade97 | ||
|
|
9708685506 | ||
|
|
28b6bf8603 | ||
|
|
2713f0e610 | ||
|
|
f7d1cd2a4f | ||
|
|
3b8963fad0 | ||
|
|
060682a1ac | ||
|
|
95cd48571e | ||
|
|
3d619e6a98 | ||
|
|
edb7e5f323 | ||
|
|
0221034b60 | ||
|
|
9500384100 | ||
|
|
0b31f2eb41 | ||
|
|
b3a6393c91 | ||
|
|
5b5599128a | ||
|
|
cb0f03ca9c | ||
|
|
2e35f3608b | ||
|
|
16c5a5a3a6 | ||
|
|
2e7db502a9 | ||
|
|
d29b71ec45 | ||
|
|
9cd0af914a | ||
|
|
4a575393f0 | ||
|
|
76c93c767d | ||
|
|
e69337a1fc | ||
|
|
50734af6cd | ||
|
|
81b0922c93 | ||
|
|
d4f8fff7af | ||
|
|
bd61f5d591 | ||
|
|
016c3d779b | ||
|
|
acf26940d6 | ||
|
|
4bafae881f | ||
|
|
8311451420 | ||
|
|
4b75cb8357 | ||
|
|
bf16d360af | ||
|
|
38d95b4faf | ||
|
|
b06e7d3cf4 | ||
|
|
9e7ea82f94 | ||
|
|
d74fc8a5d5 | ||
|
|
4a43b45a99 | ||
|
|
d6a72fbfc4 | ||
|
|
679ffbcce7 | ||
|
|
637a923e84 | ||
|
|
1f8d569b79 | ||
|
|
93ae24e707 | ||
|
|
7dd340f0b6 | ||
|
|
1d0d8d7fbe | ||
|
|
6de8d2684a | ||
|
|
9763a43943 | ||
|
|
60edbcd5f0 | ||
|
|
0ae1e40d79 | ||
|
|
34d931573c | ||
|
|
721365578a | ||
|
|
7be02318e0 | ||
|
|
88db79188c | ||
|
|
8a0329b23d | ||
|
|
02ebe59d2f | ||
|
|
6e8e053b88 | ||
|
|
fc3056b0e0 | ||
|
|
4274a8ed68 | ||
|
|
8b16cd1b36 | ||
|
|
5148e27448 | ||
|
|
608e55c01f | ||
|
|
b8963d272a | ||
|
|
beaaed6613 | ||
|
|
6bbd8c9b16 | ||
|
|
872ffa02ce | ||
|
|
b933202694 | ||
|
|
49cf0c8a9a | ||
|
|
83ccf4928f | ||
|
|
28b0d34bff | ||
|
|
0a0837ea02 | ||
|
|
a0aa350a08 | ||
|
|
decfcb6c27 | ||
|
|
730913bec4 | ||
|
|
f8f037196e | ||
|
|
e2ffeab8fa | ||
|
|
04d834187b | ||
|
|
33b2a94d90 | ||
|
|
ce3b024fea | ||
|
|
a02aa7586b | ||
|
|
d5107f2ef6 | ||
|
|
5b63b0b398 | ||
|
|
fc577241bd | ||
|
|
bb8a0e596c | ||
|
|
2a63b703f9 | ||
|
|
bfeff78164 | ||
|
|
4826289020 | ||
|
|
0aebf37ef8 | ||
|
|
d1a09d0b95 | ||
|
|
7b00003958 | ||
|
|
4483bb147c | ||
|
|
ef31c0c0da | ||
|
|
76c885f080 | ||
|
|
f16e93bd3a | ||
|
|
05d2a96900 | ||
|
|
9c70c35669 | ||
|
|
9d54c41a2b | ||
|
|
3464fbb2e8 | ||
|
|
d51d6517be | ||
|
|
5f6cc1281e | ||
|
|
035fc69060 | ||
|
|
34baf44534 | ||
|
|
c3448033de | ||
|
|
aee9b6a951 | ||
|
|
75e5bec962 | ||
|
|
59c269c8d0 | ||
|
|
541022cdc3 | ||
|
|
527521328f | ||
|
|
917b89e44f | ||
|
|
87862f3e23 | ||
|
|
10eed05d87 | ||
|
|
f5802fee31 | ||
|
|
cf9c8cbb4f | ||
|
|
f199ecf8e9 | ||
|
|
3bdd551d40 | ||
|
|
4a7936a51d | ||
|
|
76e00c2432 | ||
|
|
b46f3bf2c4 | ||
|
|
f7b4b782bf | ||
|
|
60c535e861 | ||
|
|
d59c522f7f | ||
|
|
9f798559cf | ||
|
|
f939e59463 | ||
|
|
50e89ad98b | ||
|
|
c0c5978028 | ||
|
|
abbeed394e | ||
|
|
f5b8c15388 | ||
|
|
f53b6b550f | ||
|
|
00e55b1874 | ||
|
|
90954dac49 | ||
|
|
6217523cc8 | ||
|
|
27ccd3dfa8 | ||
|
|
235f4f10ef | ||
|
|
945e5a2dc3 | ||
|
|
4b6a2685d0 | ||
|
|
e76b6c3bde | ||
|
|
4630d175d7 | ||
|
|
27055b96e3 | ||
|
|
0ef96c0bca | ||
|
|
b2be4a7d67 | ||
|
|
a70df067bc | ||
|
|
2d92b08404 | ||
|
|
4bbc57b0dc | ||
|
|
756c14d988 | ||
|
|
b3b55210f7 | ||
|
|
58093a9438 | ||
|
|
ed33dd2127 | ||
|
|
d4f9c97cca | ||
|
|
f731c1080d | ||
|
|
fd18185ef0 | ||
|
|
0efbbed5e2 | ||
|
|
bad350e49b | ||
|
|
172b93d07f | ||
|
|
ade8c162cd | ||
|
|
79e634316d | ||
|
|
dfba6c7c91 | ||
|
|
e06a77af28 | ||
|
|
74973e73e6 | ||
|
|
ac07ac5234 | ||
|
|
f4880d0519 | ||
|
|
375f992a0c | ||
|
|
ae1c5342f2 | ||
|
|
97ccb7df94 | ||
|
|
a818199b5a | ||
|
|
6fe1fa3455 | ||
|
|
aab95444a8 | ||
|
|
40f28be3b4 | ||
|
|
911d442340 | ||
|
|
d5594b03e3 | ||
|
|
89f1ddf4d7 | ||
|
|
6cfd4637db | ||
|
|
8803e11945 | ||
|
|
b1ca2cc2b6 | ||
|
|
9a8f3d7bad | ||
|
|
9d0e762f36 | ||
|
|
abf4cd71ba | ||
|
|
d66270eef0 | ||
|
|
07ecd13554 | ||
|
|
f1ff88f452 | ||
|
|
d92272ffa0 | ||
|
|
dfa43f3c5a | ||
|
|
259c5ef3d0 | ||
|
|
a1b59d4545 | ||
|
|
58a61051b9 | ||
|
|
51777c3f33 | ||
|
|
3767e9fae9 | ||
|
|
4bbfc8ccc5 | ||
|
|
531b8214c0 | ||
|
|
5cab618bf7 | ||
|
|
3380f4d11c | ||
|
|
4bf030993a | ||
|
|
f65f949a36 | ||
|
|
2864abd8c2 | ||
|
|
9bd2cb3c7e | ||
|
|
35cd277fcf | ||
|
|
57b1932b5e | ||
|
|
e6818023a3 | ||
|
|
35a541f99b | ||
|
|
5fb00a947c | ||
|
|
6288f679b9 | ||
|
|
e766759b8c | ||
|
|
c1d28381e8 | ||
|
|
2fa8371bae | ||
|
|
a1cfdf1a5b | ||
|
|
e9c7f5d664 | ||
|
|
c85f12fe2c | ||
|
|
13e5644c89 | ||
|
|
eac029aef4 | ||
|
|
a5195920fa | ||
|
|
5676a13290 | ||
|
|
d11f0e864e | ||
|
|
df83fcc5b9 | ||
|
|
f21c756793 | ||
|
|
4b07ee2fa8 | ||
|
|
ae3a39ee65 | ||
|
|
e9f5bd4ac1 | ||
|
|
1f4ad732fd | ||
|
|
5637d37ee1 | ||
|
|
c370da2fef | ||
|
|
d168209721 | ||
|
|
ca0468b8d5 | ||
|
|
039d26feeb | ||
|
|
10e7b66f38 | ||
|
|
4bb47d7e01 | ||
|
|
ec80c2b9db | ||
|
|
a89418e33b | ||
|
|
0d88ff8dae | ||
|
|
4bdf9bff3a | ||
|
|
7fbb8838e7 | ||
|
|
366ea63209 | ||
|
|
6c0ad7fe1a | ||
|
|
ef9c90a43a | ||
|
|
239214ef92 | ||
|
|
b0057b130e | ||
|
|
d64c043838 | ||
|
|
dd3599f5b3 | ||
|
|
0d56127758 | ||
|
|
ea043517c5 | ||
|
|
b84d9c5d55 | ||
|
|
6c628afe5d | ||
|
|
abc99c7e69 | ||
|
|
fe25cd3bec | ||
|
|
2eb51edfb6 | ||
|
|
715d564028 | ||
|
|
989b704efc | ||
|
|
0bfaeb8521 | ||
|
|
3db00534c2 | ||
|
|
b713b324f9 | ||
|
|
6512dbae1c | ||
|
|
3afda71349 | ||
|
|
568e5a9bb8 | ||
|
|
89e56ae279 | ||
|
|
339ac05443 | ||
|
|
72cfa683cf | ||
|
|
3a6b9f04f9 | ||
|
|
59f24df294 | ||
|
|
5c559af936 | ||
|
|
bb80505b76 | ||
|
|
a560f6e9f6 | ||
|
|
95ae981698 | ||
|
|
969eb67217 | ||
|
|
0dfebbad9d | ||
|
|
f87f4bd8cc | ||
|
|
352caa85da | ||
|
|
8f61e9876f | ||
|
|
fd19bb7cd5 | ||
|
|
0c2e9137a2 | ||
|
|
bf5a25a96f | ||
|
|
aa84f21fde | ||
|
|
b9de2b4b58 | ||
|
|
9754f2d1c5 | ||
|
|
f66fc06b4f | ||
|
|
79ceb56c60 | ||
|
|
7605df1bd9 | ||
|
|
b91ec48178 | ||
|
|
3c2f144795 | ||
|
|
0271337f8e | ||
|
|
630a71c46c | ||
|
|
150329dd4a | ||
|
|
59d7bce518 | ||
|
|
3c1e3cd38e | ||
|
|
a2eb0bf9fe | ||
|
|
5d48ecf86a | ||
|
|
00d09aa01e | ||
|
|
9afdc55416 | ||
|
|
2c942c8809 | ||
|
|
c15acc4ce3 | ||
|
|
b056610eaa | ||
|
|
8eb9fb1834 | ||
|
|
7ec518b41c | ||
|
|
dc15914a85 | ||
|
|
3b22f59988 | ||
|
|
afdab0300e | ||
|
|
26533c47e7 | ||
|
|
df3aeed291 | ||
|
|
867ba7b68f | ||
|
|
1679a3f844 | ||
|
|
1611049623 | ||
|
|
7d195367a8 | ||
|
|
88a4f25689 | ||
|
|
161dee89ec | ||
|
|
34af33607b | ||
|
|
5bb188a822 | ||
|
|
60bb6f105d | ||
|
|
df4680ee09 | ||
|
|
6aaab09601 | ||
|
|
5da42575fd | ||
|
|
fe256d6a62 | ||
|
|
5f175141e1 | ||
|
|
983e2df065 | ||
|
|
16d5a70c08 | ||
|
|
b7e2d7fb8e | ||
|
|
fb5f7a336d | ||
|
|
78dc5f4bf4 | ||
|
|
45dbf5393f | ||
|
|
9fed1cde25 | ||
|
|
5c7b175e90 | ||
|
|
30b29de8ce | ||
|
|
a5f9331023 | ||
|
|
d8b9d8431e | ||
|
|
91a2ce2b3f | ||
|
|
4da1871567 | ||
|
|
e809f77461 | ||
|
|
e96d23cc3f | ||
|
|
c34e2ab3e1 | ||
|
|
820519b4f7 | ||
|
|
34688852a4 | ||
|
|
151f28081a | ||
|
|
213a64b1ff | ||
|
|
f259d81249 | ||
|
|
589761bfd9 | ||
|
|
18fde86a20 | ||
|
|
ba28bc94d3 | ||
|
|
da19a07943 | ||
|
|
ecc500fc91 | ||
|
|
c22ac1e60a | ||
|
|
55d9aa2a4c | ||
|
|
1d391e68e5 | ||
|
|
0429c44d18 | ||
|
|
2c1bcaafc1 | ||
|
|
35891c74cd | ||
|
|
2ca6e67b37 | ||
|
|
6e72be54cb | ||
|
|
07edb998e4 | ||
|
|
3e52f804a7 | ||
|
|
75b7583832 | ||
|
|
d754eb74f7 | ||
|
|
60252267d5 | ||
|
|
b25af641e2 | ||
|
|
e7c3f8bf47 | ||
|
|
4c1dca73c4 | ||
|
|
0bbb6b91fe | ||
|
|
ee93d9b495 | ||
|
|
bf8ac214a1 | ||
|
|
9c7b34d5e6 | ||
|
|
76c0fa2fe2 | ||
|
|
ac3a17b178 | ||
|
|
c76b527b93 | ||
|
|
ded4f95537 | ||
|
|
8272386733 | ||
|
|
33988ed3fb | ||
|
|
411b8e3cb6 | ||
|
|
916da16523 | ||
|
|
d165c081f7 | ||
|
|
992de7d66e | ||
|
|
46ab7bbcbe | ||
|
|
b04bced37f | ||
|
|
13335cadc6 | ||
|
|
b864791fa6 | ||
|
|
6614b56298 | ||
|
|
02c3894fc9 | ||
|
|
68f7dc9512 | ||
|
|
18d1bc56fd | ||
|
|
1e4d07a52c | ||
|
|
1fc579e907 | ||
|
|
827c4e31ee | ||
|
|
c2a1ed926e | ||
|
|
4f86c117c3 | ||
|
|
93817ba92f | ||
|
|
18153e0fcc | ||
|
|
3123f6444f | ||
|
|
4e97a3b3d5 | ||
|
|
134c43ad9e | ||
|
|
e74b4b35b9 | ||
|
|
16e7194dfe | ||
|
|
932b0ccf24 | ||
|
|
3e5c7f62d0 | ||
|
|
bf19f5b9c0 | ||
|
|
08a879bbb1 | ||
|
|
cd514285d9 | ||
|
|
782bb11894 | ||
|
|
355689ed19 | ||
|
|
75614fb13c | ||
|
|
5c4a864680 | ||
|
|
eaeff891d6 | ||
|
|
f0ab40d748 | ||
|
|
e497af4c26 | ||
|
|
f860f57363 | ||
|
|
02bf5ada89 | ||
|
|
d3b578fe8f | ||
|
|
d29d910ac6 | ||
|
|
e7b41f9a4c | ||
|
|
dd0aed4614 | ||
|
|
b9b4f2bb7f | ||
|
|
3533d2a2cc | ||
|
|
26d9ef5398 | ||
|
|
a0f840bcf8 | ||
|
|
33d2a77e37 | ||
|
|
80e00a80d5 | ||
|
|
a3d5479878 | ||
|
|
a49dc04f5d | ||
|
|
d1c0c9739d | ||
|
|
7415b07586 | ||
|
|
023663b268 | ||
|
|
3883c509b9 | ||
|
|
18f34b4f83 | ||
|
|
caed86d846 | ||
|
|
459e36c027 | ||
|
|
725f8571bb | ||
|
|
b7c7c0e862 | ||
|
|
3f671b918a | ||
|
|
9492363b22 | ||
|
|
3ee144459f | ||
|
|
c0c80c0fdf | ||
|
|
7c80b61666 | ||
|
|
d128f3e14e | ||
|
|
4498b89ac4 | ||
|
|
e576a58ead | ||
|
|
eb4375258e | ||
|
|
0cbc2001e2 | ||
|
|
6bf5dbabee | ||
|
|
6a89646e66 | ||
|
|
f3234a6b5e | ||
|
|
73a8c302e9 | ||
|
|
989f2d3001 | ||
|
|
2badcfa546 | ||
|
|
384e14b32d | ||
|
|
016e743653 | ||
|
|
00adf3631a | ||
|
|
09aef18999 | ||
|
|
1d86aac338 | ||
|
|
80173634a0 | ||
|
|
c9598b674c | ||
|
|
2a588d1e9a | ||
|
|
5a6c06c8a3 | ||
|
|
b2ef4e9619 | ||
|
|
9e9d6e45b4 | ||
|
|
6752457ad8 | ||
|
|
ddcb5cd4d3 | ||
|
|
a54b2db81b | ||
|
|
6740124364 | ||
|
|
157731e4f8 | ||
|
|
2dd1496ef4 | ||
|
|
d1e4e72693 | ||
|
|
77e8143290 | ||
|
|
7f791d4919 | ||
|
|
d7e0468776 | ||
|
|
f6c611bbba | ||
|
|
0990ac4fc1 | ||
|
|
e91f8f693b | ||
|
|
2a7dbda133 | ||
|
|
793e542312 | ||
|
|
240269eb25 | ||
|
|
c744dc8cc3 | ||
|
|
d596bdb454 | ||
|
|
bec54b4283 | ||
|
|
061b88f5b5 | ||
|
|
8704eff632 | ||
|
|
fb16f25b07 | ||
|
|
e8057a5c8a | ||
|
|
3c5edb6171 | ||
|
|
e36a191240 | ||
|
|
ecdfd65f50 | ||
|
|
4294081abb | ||
|
|
5218543c58 | ||
|
|
d8332a27e5 | ||
|
|
673658dfd2 | ||
|
|
6528d3d7da | ||
|
|
16af479b83 | ||
|
|
13187de97d | ||
|
|
32850f6770 | ||
|
|
4a7f4bde4a | ||
|
|
0010119440 | ||
|
|
91065a6168 | ||
|
|
efa8d5c575 | ||
|
|
04998d0215 | ||
|
|
d0efa5d3fe | ||
|
|
c87e72e08e | ||
|
|
efb82847cb | ||
|
|
f37e267a5e | ||
|
|
69928219a3 | ||
|
|
fdf8845a2f | ||
|
|
4073a7abc3 | ||
|
|
ffd9a34cf5 | ||
|
|
07226c6d21 | ||
|
|
b1bc7c1fc2 | ||
|
|
1b33f0cea9 | ||
|
|
8ece3b00f5 | ||
|
|
c9c58b65a6 | ||
|
|
66becbc4cc | ||
|
|
5b8612c919 | ||
|
|
430c22e06e | ||
|
|
76b62eda3a | ||
|
|
bc983162f3 | ||
|
|
4922598aee | ||
|
|
b2f8bb9990 | ||
|
|
9ee92fb9e9 | ||
|
|
981bf1d56f | ||
|
|
d2c2503cfa | ||
|
|
2a4caa856e | ||
|
|
157962e42a | ||
|
|
16db28060c | ||
|
|
712424c339 | ||
|
|
15c56dfcb8 | ||
|
|
b98ad47618 | ||
|
|
4778c5f5e8 | ||
|
|
d041671dc5 | ||
|
|
5cab65d197 | ||
|
|
b5bf627fb1 | ||
|
|
6104150b77 | ||
|
|
a13bae2f39 | ||
|
|
fd80e98207 | ||
|
|
f43b95f001 | ||
|
|
38802d3522 | ||
|
|
9f7813622d | ||
|
|
75d67207aa | ||
|
|
e596a8f731 | ||
|
|
5b0cc73792 | ||
|
|
2163d4465f | ||
|
|
8becf45714 | ||
|
|
853ead26ca | ||
|
|
0ccb6cb873 | ||
|
|
e46ff3de8b | ||
|
|
a02e08a879 | ||
|
|
109d7d87bd | ||
|
|
3df740702c | ||
|
|
06bb6f7bff | ||
|
|
e33738a876 | ||
|
|
f887f5dca3 | ||
|
|
257c16690a | ||
|
|
7114e88992 | ||
|
|
de7e869ca9 | ||
|
|
eaee9c9522 | ||
|
|
bee11a6d41 | ||
|
|
647e44147f | ||
|
|
4a5d46915d | ||
|
|
8ad19585cb | ||
|
|
951a33fae9 | ||
|
|
bcf174cd1e | ||
|
|
1e82909f58 | ||
|
|
33e83b8414 | ||
|
|
592c56cb20 | ||
|
|
710528c01a | ||
|
|
03cadc2604 | ||
|
|
dd73bb95a4 | ||
|
|
40e0a55378 | ||
|
|
269884d9f3 | ||
|
|
fcd548c313 | ||
|
|
140a8b6804 | ||
|
|
b5378c1296 | ||
|
|
1445d6ea8c | ||
|
|
5385431051 | ||
|
|
f14f4498fb | ||
|
|
fc2786f5e8 | ||
|
|
174dbb5e74 | ||
|
|
68517c15f2 | ||
|
|
11ee142e4b | ||
|
|
1e1d047e07 | ||
|
|
62f1e39e6e | ||
|
|
05756da495 | ||
|
|
fa35b2a66f | ||
|
|
d2094e2b68 | ||
|
|
075b2df738 | ||
|
|
ec3c31a106 | ||
|
|
e2183c2214 | ||
|
|
4994064e6e | ||
|
|
a40b9f4054 | ||
|
|
ee8d65977d | ||
|
|
b4ea11e55e | ||
|
|
b8d2ef1eb5 | ||
|
|
0efeffeaa3 | ||
|
|
7a86d272bb | ||
|
|
4d780904d1 | ||
|
|
0a6fa65075 | ||
|
|
6440220640 | ||
|
|
4350c9a72b | ||
|
|
77feae0ab9 | ||
|
|
1261696ba2 | ||
|
|
6b35a0a527 | ||
|
|
fd33ff81c9 | ||
|
|
178b1e8bb0 | ||
|
|
9d50f03cb1 | ||
|
|
8c1688657a | ||
|
|
833cb99f41 | ||
|
|
bd5d84abcd | ||
|
|
d451ba9b6e | ||
|
|
14bd02a569 | ||
|
|
4beace1bb0 | ||
|
|
6996dfcd3b | ||
|
|
42c46d7d5c | ||
|
|
9b31ce83c5 | ||
|
|
cb5250527b | ||
|
|
f0b73fd696 | ||
|
|
df5684a9f8 | ||
|
|
b3f724c799 | ||
|
|
a7be6504a2 | ||
|
|
1da5357df6 | ||
|
|
92e1847c59 | ||
|
|
0500994def | ||
|
|
da911bfeb8 | ||
|
|
578d673a4e | ||
|
|
c8e58a1e5b | ||
|
|
d477874535 | ||
|
|
da79386cc3 | ||
|
|
a4ba6d1444 | ||
|
|
ef28459b61 | ||
|
|
1ff8c908b8 | ||
|
|
e966ef96e5 | ||
|
|
b05b38b269 | ||
|
|
8e1f1ff2e6 | ||
|
|
680d6c20ca | ||
|
|
c886e7949e | ||
|
|
e0b972f6d6 | ||
|
|
25daa9f2da | ||
|
|
d0fb5c3bd5 | ||
|
|
520b12e56b | ||
|
|
5c8ffe961e | ||
|
|
7983e82b60 | ||
|
|
77d35b61a9 | ||
|
|
285a97aaf8 | ||
|
|
ad29f2477e | ||
|
|
1072d1306b | ||
|
|
b8eda40937 | ||
|
|
2719ae5df2 | ||
|
|
68ee2bdcdc | ||
|
|
da654fdff5 | ||
|
|
d7f9d5a66f | ||
|
|
c4fb7b7928 | ||
|
|
43a791db65 | ||
|
|
217311211a | ||
|
|
ca55890ad2 | ||
|
|
d6ecf5b8a9 | ||
|
|
e52edde11f | ||
|
|
2e514735ec | ||
|
|
3d32c30d2d | ||
|
|
05235f8385 | ||
|
|
cd28a75c86 | ||
|
|
34075738ea | ||
|
|
88c0b8a8f0 | ||
|
|
e8bbc117e1 | ||
|
|
b99f45874f | ||
|
|
0dfa378e38 | ||
|
|
3da0c07bcd | ||
|
|
2196b53075 | ||
|
|
a8340f37bb | ||
|
|
7b1710ee63 | ||
|
|
38b7d9724e | ||
|
|
2b1ed49e9a | ||
|
|
017cf9e464 | ||
|
|
781f0c843e | ||
|
|
e2bf474332 | ||
|
|
7e2f1c9a8b | ||
|
|
8e798dde48 | ||
|
|
c05ae6e94c | ||
|
|
ff28ea8fa8 | ||
|
|
7914e89212 | ||
|
|
558ff90e27 | ||
|
|
ee69653a83 | ||
|
|
95339a8338 | ||
|
|
39b1435725 | ||
|
|
b1d3e258bd | ||
|
|
6ff7fa74e2 | ||
|
|
91305262f1 | ||
|
|
6d16b68f11 | ||
|
|
f22e4f1cc7 | ||
|
|
2851d12357 | ||
|
|
73968e4277 | ||
|
|
7a6ecd86c6 | ||
|
|
1ff2a08d19 | ||
|
|
d1efd62e7b | ||
|
|
366c95cd3c | ||
|
|
7e03f3958e | ||
|
|
e069184721 | ||
|
|
5182441cb3 | ||
|
|
0de55a8ff5 | ||
|
|
0900d7c764 | ||
|
|
8540e09ba7 | ||
|
|
6e301601f9 | ||
|
|
1bf0eab2d9 | ||
|
|
d560f656f4 | ||
|
|
0bc256aa23 | ||
|
|
ebc073a52e | ||
|
|
23503dc439 | ||
|
|
1f4985c7dd | ||
|
|
ed88d9e10d | ||
|
|
906196bac3 | ||
|
|
cb9751be04 | ||
|
|
09e03d579b | ||
|
|
9fcca2698e | ||
|
|
bba8cd9b58 | ||
|
|
c8bb30289a | ||
|
|
69f56c1eb7 | ||
|
|
adc8e23356 | ||
|
|
88005c6603 | ||
|
|
33ee4c36b4 | ||
|
|
1a142b7fe6 | ||
|
|
90373806c0 | ||
|
|
a47634bf49 | ||
|
|
e03e58323b | ||
|
|
ab1e31c0e7 | ||
|
|
aa5505d693 | ||
|
|
74f8f687cf | ||
|
|
d54500bad5 | ||
|
|
4966b4d58d | ||
|
|
b75a4667c2 | ||
|
|
14579a9320 | ||
|
|
1d92eff974 | ||
|
|
91274267e5 | ||
|
|
c49f0ede16 | ||
|
|
42a0f452b1 | ||
|
|
c24ab9831a | ||
|
|
506a68ee6a | ||
|
|
1291f792ab | ||
|
|
51cfb1903d | ||
|
|
1b1c15694a | ||
|
|
251f1941a9 | ||
|
|
625617bb20 | ||
|
|
f80f161886 | ||
|
|
4b4889d5f2 | ||
|
|
fee34ba257 | ||
|
|
c29ab25dd2 | ||
|
|
e0308a11c9 | ||
|
|
a738998c2c | ||
|
|
6be22c474d | ||
|
|
da19743ba5 |
@@ -13,3 +13,6 @@ max_line_length = 100
|
|||||||
[*.md]
|
[*.md]
|
||||||
max_line_length = off
|
max_line_length = off
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.rs]
|
||||||
|
indent_size = 4
|
||||||
43
.github/workflows/daedalus-docker.yml
vendored
Normal file
43
.github/workflows/daedalus-docker.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: daedalus-docker-build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
paths:
|
||||||
|
- .github/workflows/daedalus-docker.yml
|
||||||
|
- 'apps/daedalus_client/**'
|
||||||
|
pull_request:
|
||||||
|
types: [ opened, synchronize ]
|
||||||
|
paths:
|
||||||
|
- .github/workflows/daedalus-docker.yml
|
||||||
|
- 'apps/daedalus_client/**'
|
||||||
|
merge_group:
|
||||||
|
types: [ checks_requested ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Fetch docker metadata
|
||||||
|
id: docker_meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: ghcr.io/modrinth/daedalus
|
||||||
|
-
|
||||||
|
name: Login to GitHub Images
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
-
|
||||||
|
name: Build and push
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
file: ./apps/daedalus_client/Dockerfile
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||||
52
.github/workflows/daedalus-run.yml
vendored
Normal file
52
.github/workflows/daedalus-run.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: Run Meta
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '*/5 * * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run-docker:
|
||||||
|
if: github.repository_owner == 'modrinth'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
- name: Pull Docker image from GHCR
|
||||||
|
run: docker pull ghcr.io/modrinth/daedalus:main
|
||||||
|
|
||||||
|
- name: Run Docker container
|
||||||
|
env:
|
||||||
|
BASE_URL: ${{ secrets.BASE_URL }}
|
||||||
|
S3_ACCESS_TOKEN: ${{ secrets.S3_ACCESS_TOKEN }}
|
||||||
|
S3_SECRET: ${{ secrets.S3_SECRET }}
|
||||||
|
S3_URL: ${{ secrets.S3_URL }}
|
||||||
|
S3_REGION: ${{ secrets.S3_REGION }}
|
||||||
|
S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
|
||||||
|
CLOUDFLARE_INTEGRATION: ${{ secrets.CLOUDFLARE_INTEGRATION }}
|
||||||
|
CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }}
|
||||||
|
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
|
||||||
|
run: |
|
||||||
|
docker run \
|
||||||
|
--name daedalus \
|
||||||
|
-e RUST_LOG=warn,daedalus_client=trace \
|
||||||
|
-e BASE_URL=$BASE_URL \
|
||||||
|
-e S3_ACCESS_TOKEN=$S3_ACCESS_TOKEN \
|
||||||
|
-e S3_SECRET=$S3_SECRET \
|
||||||
|
-e S3_URL=$S3_URL \
|
||||||
|
-e S3_REGION=$S3_REGION \
|
||||||
|
-e S3_BUCKET_NAME=$S3_BUCKET_NAME \
|
||||||
|
-e CLOUDFLARE_INTEGRATION=$CLOUDFLARE_INTEGRATION \
|
||||||
|
-e CLOUDFLARE_TOKEN=$CLOUDFLARE_TOKEN \
|
||||||
|
-e CLOUDFLARE_ZONE_ID=$CLOUDFLARE_ZONE_ID \
|
||||||
|
ghcr.io/modrinth/daedalus:main
|
||||||
8
.github/workflows/frontend-pages.yml
vendored
8
.github/workflows/frontend-pages.yml
vendored
@@ -1,6 +1,9 @@
|
|||||||
name: Deploy frontend
|
name: Clear pages cache
|
||||||
|
|
||||||
on: push
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- prod
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
wait:
|
wait:
|
||||||
@@ -16,7 +19,6 @@ jobs:
|
|||||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||||
accountId: '9ddae624c98677d68d93df6e524a6061'
|
accountId: '9ddae624c98677d68d93df6e524a6061'
|
||||||
project: 'frontend'
|
project: 'frontend'
|
||||||
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
commitHash: ${{ steps.push-changes.outputs.commit-hash }}
|
commitHash: ${{ steps.push-changes.outputs.commit-hash }}
|
||||||
- name: Purge cache
|
- name: Purge cache
|
||||||
if: github.ref == 'refs/heads/prod'
|
if: github.ref == 'refs/heads/prod'
|
||||||
|
|||||||
46
.github/workflows/labrinth-docker.yml
vendored
Normal file
46
.github/workflows/labrinth-docker.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
name: docker-build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
paths:
|
||||||
|
- .github/workflows/labrinth-docker.yml
|
||||||
|
- 'apps/labrinth/**'
|
||||||
|
pull_request:
|
||||||
|
types: [ opened, synchronize ]
|
||||||
|
paths:
|
||||||
|
- .github/workflows/labrinth-docker.yml
|
||||||
|
- 'apps/labrinth/**'
|
||||||
|
merge_group:
|
||||||
|
types: [ checks_requested ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./apps/labrinth
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Fetch docker metadata
|
||||||
|
id: docker_meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: ghcr.io/modrinth/labrinth
|
||||||
|
-
|
||||||
|
name: Login to GitHub Images
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
-
|
||||||
|
name: Build and push
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: ./apps/labrinth
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||||
@@ -6,6 +6,7 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
paths:
|
paths:
|
||||||
|
- .github/workflows/app-release.yml
|
||||||
- 'apps/app/**'
|
- 'apps/app/**'
|
||||||
- 'apps/app-frontend/**'
|
- 'apps/app-frontend/**'
|
||||||
- 'packages/app-lib/**'
|
- 'packages/app-lib/**'
|
||||||
@@ -20,12 +21,12 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
platform: [macos-latest, windows-latest, ubuntu-20.04]
|
platform: [macos-latest, windows-latest, ubuntu-22.04]
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Rust setup (mac)
|
- name: Rust setup (mac)
|
||||||
if: startsWith(matrix.platform, 'macos')
|
if: startsWith(matrix.platform, 'macos')
|
||||||
@@ -43,13 +44,34 @@ jobs:
|
|||||||
- name: Setup rust cache
|
- name: Setup rust cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: target/**
|
path: |
|
||||||
|
target/**
|
||||||
|
!target/*/release/bundle/*/*.dmg
|
||||||
|
!target/*/release/bundle/*/*.app.tar.gz
|
||||||
|
!target/*/release/bundle/*/*.app.tar.gz.sig
|
||||||
|
!target/release/bundle/*/*.dmg
|
||||||
|
!target/release/bundle/*/*.app.tar.gz
|
||||||
|
!target/release/bundle/*/*.app.tar.gz.sig
|
||||||
|
|
||||||
|
!target/release/bundle/*/*.AppImage
|
||||||
|
!target/release/bundle/*/*.AppImage.tar.gz
|
||||||
|
!target/release/bundle/*/*.AppImage.tar.gz.sig
|
||||||
|
!target/release/bundle/*/*.deb
|
||||||
|
!target/release/bundle/*/*.rpm
|
||||||
|
|
||||||
|
!target/release/bundle/msi/*.msi
|
||||||
|
!target/release/bundle/msi/*.msi.zip
|
||||||
|
!target/release/bundle/msi/*.msi.zip.sig
|
||||||
|
|
||||||
|
!target/release/bundle/nsis/*.exe
|
||||||
|
!target/release/bundle/nsis/*.nsis.zip
|
||||||
|
!target/release/bundle/nsis/*.nsis.zip.sig
|
||||||
key: ${{ runner.os }}-rust-target-${{ hashFiles('**/Cargo.lock') }}
|
key: ${{ runner.os }}-rust-target-${{ hashFiles('**/Cargo.lock') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-rust-target-
|
${{ runner.os }}-rust-target-
|
||||||
|
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
@@ -66,7 +88,7 @@ jobs:
|
|||||||
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@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
@@ -77,14 +99,13 @@ jobs:
|
|||||||
if: startsWith(matrix.platform, 'ubuntu')
|
if: startsWith(matrix.platform, 'ubuntu')
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libselinux1
|
sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev pkg-config libayatana-appindicator3-dev librsvg2-dev
|
||||||
|
|
||||||
- name: Install frontend dependencies
|
- name: Install frontend dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: build app (macos)
|
- name: build app (macos)
|
||||||
uses: tauri-apps/tauri-action@v0
|
run: pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config "tauri-release.conf.json"
|
||||||
id: build_os_mac
|
|
||||||
if: startsWith(matrix.platform, 'macos')
|
if: startsWith(matrix.platform, 'macos')
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -95,34 +116,40 @@ jobs:
|
|||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
with:
|
|
||||||
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
|
run: pnpm --filter=@modrinth/app run tauri build --config "tauri-release.conf.json"
|
||||||
id: build_os
|
id: build_os
|
||||||
if: "!startsWith(matrix.platform, 'macos')"
|
if: "!startsWith(matrix.platform, 'macos')"
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
TAURI_SIGNING_PRIVATE_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@v4
|
||||||
if: startsWith(matrix.platform, 'macos')
|
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.platform }}
|
name: ${{ matrix.platform }}
|
||||||
path: "${{ join(fromJSON(steps.build_os_mac.outputs.artifactPaths), '\n') }}"
|
path: |
|
||||||
|
target/*/release/bundle/*/*.dmg
|
||||||
|
target/*/release/bundle/*/*.app.tar.gz
|
||||||
|
target/*/release/bundle/*/*.app.tar.gz.sig
|
||||||
|
target/release/bundle/*/*.dmg
|
||||||
|
target/release/bundle/*/*.app.tar.gz
|
||||||
|
target/release/bundle/*/*.app.tar.gz.sig
|
||||||
|
|
||||||
- name: upload ${{ matrix.platform }}
|
target/release/bundle/*/*.AppImage
|
||||||
uses: actions/upload-artifact@v3
|
target/release/bundle/*/*.AppImage.tar.gz
|
||||||
if: "!startsWith(matrix.platform, 'macos')"
|
target/release/bundle/*/*.AppImage.tar.gz.sig
|
||||||
with:
|
target/release/bundle/*/*.deb
|
||||||
name: ${{ matrix.platform }}
|
target/release/bundle/*/*.rpm
|
||||||
path: "${{ join(fromJSON(steps.build_os.outputs.artifactPaths), '\n') }}"
|
|
||||||
|
target/release/bundle/msi/*.msi
|
||||||
|
target/release/bundle/msi/*.msi.zip
|
||||||
|
target/release/bundle/msi/*.msi.zip.sig
|
||||||
|
|
||||||
|
target/release/bundle/nsis/*.exe
|
||||||
|
target/release/bundle/nsis/*.nsis.zip
|
||||||
|
target/release/bundle/nsis/*.nsis.zip.sig
|
||||||
@@ -11,7 +11,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build, Test, and Lint
|
name: Build, Test, and Lint
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libselinux1
|
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||||
|
|
||||||
- name: Setup Node.JS environment
|
- name: Setup Node.JS environment
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@@ -62,9 +62,19 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
env:
|
||||||
|
SQLX_OFFLINE: true
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: pnpm lint
|
run: pnpm lint
|
||||||
|
env:
|
||||||
|
SQLX_OFFLINE: true
|
||||||
|
|
||||||
|
- name: Start docker compose
|
||||||
|
run: docker compose up -d
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: pnpm test
|
run: pnpm test
|
||||||
|
env:
|
||||||
|
SQLX_OFFLINE: true
|
||||||
|
DATABASE_URL: postgresql://labrinth:labrinth@localhost/postgres
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -55,3 +55,9 @@ generated
|
|||||||
|
|
||||||
# app testing dir
|
# app testing dir
|
||||||
app-playground-data/*
|
app-playground-data/*
|
||||||
|
|
||||||
|
# soley because i need the PORT to be 3002 due to WSL stuff
|
||||||
|
.env
|
||||||
|
apps/frontend/.env
|
||||||
|
|
||||||
|
.astro
|
||||||
|
|||||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
15
.idea/daedalus.iml
generated
Normal file
15
.idea/daedalus.iml
generated
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/daedalus_client_new/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/apps/daedalus_client/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/packages/daedalus/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
7
.idea/discord.xml
generated
Normal file
7
.idea/discord.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="PROJECT_FILES" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
26
.idea/libraries/KotlinJavaRuntime.xml
generated
Normal file
26
.idea/libraries/KotlinJavaRuntime.xml
generated
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="KotlinJavaRuntime" type="repository">
|
||||||
|
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.0/kotlin-stdlib-jdk8-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.0/kotlin-stdlib-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.0/kotlin-stdlib-common-1.8.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.0/kotlin-stdlib-jdk7-1.8.0.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.0/kotlin-stdlib-jdk8-1.8.0-javadoc.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.0/kotlin-stdlib-1.8.0-javadoc.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.0/kotlin-stdlib-common-1.8.0-javadoc.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.0/kotlin-stdlib-jdk7-1.8.0-javadoc.jar!/" />
|
||||||
|
</JAVADOC>
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.0/kotlin-stdlib-jdk8-1.8.0-sources.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.0/kotlin-stdlib-1.8.0-sources.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.0/kotlin-stdlib-common-1.8.0-sources.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.0/kotlin-stdlib-jdk7-1.8.0-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
|
</component>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/daedalus.iml" filepath="$PROJECT_DIR$/.idea/daedalus.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6707
Cargo.lock
generated
6707
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,10 @@ resolver = '2'
|
|||||||
members = [
|
members = [
|
||||||
'./packages/app-lib',
|
'./packages/app-lib',
|
||||||
'./apps/app-playground',
|
'./apps/app-playground',
|
||||||
'./apps/app'
|
'./apps/app',
|
||||||
|
'./apps/labrinth',
|
||||||
|
'./apps/daedalus_client',
|
||||||
|
'./packages/daedalus',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Optimize for speed and reduce size on release builds
|
# Optimize for speed and reduce size on release builds
|
||||||
@@ -16,3 +19,6 @@ strip = true # Remove debug symbols
|
|||||||
|
|
||||||
[profile.dev.package.sqlx-macros]
|
[profile.dev.package.sqlx-macros]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
wry = { git = "https://github.com/modrinth/wry", rev = "27fb16b" }
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ This repository contains two primary packages. For detailed development informat
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We welcome contributions! Before submitting any contributions, please read our [contributing guidelines](https://support.modrinth.com/en/articles/8802215-contributing-to-modrinth).
|
We welcome contributions! Before submitting any contributions, please read our [contributing guidelines](https://docs.modrinth.com/contributing/getting-started/).
|
||||||
|
|
||||||
If you plan to fork this repository for your own purposes, please review our [copying guidelines](COPYING.md).
|
If you plan to fork this repository for your own purposes, please review our [copying guidelines](COPYING.md).
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
extends: ['custom/vue'],
|
|
||||||
}
|
|
||||||
22
apps/app-frontend/eslint.config.mjs
Normal file
22
apps/app-frontend/eslint.config.mjs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { createConfigForNuxt } from '@nuxt/eslint-config/flat'
|
||||||
|
import { fixupPluginRules } from '@eslint/compat'
|
||||||
|
import turboPlugin from 'eslint-plugin-turbo'
|
||||||
|
|
||||||
|
export default createConfigForNuxt().append([
|
||||||
|
{
|
||||||
|
name: 'turbo',
|
||||||
|
plugins: {
|
||||||
|
turbo: fixupPluginRules(turboPlugin),
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'turbo/no-undeclared-env-vars': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'modrinth',
|
||||||
|
rules: {
|
||||||
|
'vue/html-self-closing': 'off',
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<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" />
|
||||||
<title>Modrinth App</title>
|
<title>Modrinth App</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/src/assets/stylesheets/global.scss" />
|
<link rel="stylesheet" href="/src/assets/stylesheets/global.scss" />
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@modrinth/app-frontend",
|
"name": "@modrinth/app-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.8.3-1",
|
"version": "0.8.9",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
|
"tsc:check": "vue-tsc --noEmit",
|
||||||
"lint": "eslint . && prettier --check .",
|
"lint": "eslint . && prettier --check .",
|
||||||
"fix": "eslint . --fix && prettier --write ."
|
"fix": "eslint . --fix && prettier --write ."
|
||||||
},
|
},
|
||||||
@@ -13,14 +14,19 @@
|
|||||||
"@modrinth/assets": "workspace:*",
|
"@modrinth/assets": "workspace:*",
|
||||||
"@modrinth/ui": "workspace:*",
|
"@modrinth/ui": "workspace:*",
|
||||||
"@modrinth/utils": "workspace:*",
|
"@modrinth/utils": "workspace:*",
|
||||||
"@tauri-apps/api": "^1.6.0",
|
"@sentry/vue": "^8.27.0",
|
||||||
|
"@tauri-apps/api": "^2.0.0-rc.3",
|
||||||
|
"@tauri-apps/plugin-dialog": "^2.0.0-rc.0",
|
||||||
|
"@tauri-apps/plugin-os": "^2.0.0-rc.0",
|
||||||
|
"@tauri-apps/plugin-shell": "^2.0.0-rc.0",
|
||||||
|
"@tauri-apps/plugin-updater": "^2.0.0-rc.0",
|
||||||
|
"@tauri-apps/plugin-window-state": "^2.0.0-rc.0",
|
||||||
"@vintl/vintl": "^4.4.1",
|
"@vintl/vintl": "^4.4.1",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"floating-vue": "^5.2.2",
|
"floating-vue": "^5.2.2",
|
||||||
"mixpanel-browser": "^2.49.0",
|
|
||||||
"ofetch": "^1.3.4",
|
"ofetch": "^1.3.4",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1",
|
"posthog-js": "^1.158.2",
|
||||||
"vite-svg-loader": "^5.1.0",
|
"vite-svg-loader": "^5.1.0",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
"vue-multiselect": "3.0.0",
|
"vue-multiselect": "3.0.0",
|
||||||
@@ -28,17 +34,21 @@
|
|||||||
"vue-virtual-scroller": "v2.0.0-beta.8"
|
"vue-virtual-scroller": "v2.0.0-beta.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.6.0",
|
"@eslint/compat": "^1.1.1",
|
||||||
|
"@nuxt/eslint-config": "^0.5.6",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^9.9.1",
|
||||||
"eslint-config-custom": "workspace:*",
|
"eslint-config-custom": "workspace:*",
|
||||||
|
"eslint-plugin-turbo": "^2.1.1",
|
||||||
"postcss": "^8.4.39",
|
"postcss": "^8.4.39",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"sass": "^1.74.1",
|
"sass": "^1.74.1",
|
||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.4",
|
||||||
"tsconfig": "workspace:*",
|
"tsconfig": "workspace:*",
|
||||||
"vite": "^5.2.8"
|
"typescript": "^5.5.4",
|
||||||
|
"vite": "^5.2.8",
|
||||||
|
"vue-tsc": "^2.1.6"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.4.0"
|
"packageManager": "pnpm@9.4.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, onMounted } 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 { HomeIcon, SearchIcon, LibraryIcon, PlusIcon, SettingsIcon, XIcon } from '@modrinth/assets'
|
import {
|
||||||
|
HomeIcon,
|
||||||
|
SearchIcon,
|
||||||
|
LibraryIcon,
|
||||||
|
PlusIcon,
|
||||||
|
SettingsIcon,
|
||||||
|
XIcon,
|
||||||
|
DownloadIcon,
|
||||||
|
} from '@modrinth/assets'
|
||||||
import { Button, Notifications } from '@modrinth/ui'
|
import { Button, Notifications } from '@modrinth/ui'
|
||||||
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'
|
||||||
@@ -15,28 +23,26 @@ import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
|||||||
import { handleError, useNotifications } from '@/store/notifications.js'
|
import { handleError, useNotifications } from '@/store/notifications.js'
|
||||||
import { command_listener, warning_listener } from '@/helpers/events.js'
|
import { command_listener, warning_listener } from '@/helpers/events.js'
|
||||||
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
|
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
|
||||||
import { type } from '@tauri-apps/api/os'
|
import { type } from '@tauri-apps/plugin-os'
|
||||||
import { appWindow } from '@tauri-apps/api/window'
|
import { isDev, getOS, restartApp } from '@/helpers/utils.js'
|
||||||
import { isDev, getOS } from '@/helpers/utils.js'
|
import { initAnalytics, debugAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
|
||||||
import {
|
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||||
mixpanel_track,
|
|
||||||
mixpanel_init,
|
|
||||||
mixpanel_opt_out_tracking,
|
|
||||||
mixpanel_is_loaded,
|
|
||||||
} from '@/helpers/mixpanel'
|
|
||||||
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 { TauriEvent } from '@tauri-apps/api/event'
|
|
||||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
import URLConfirmModal from '@/components/ui/URLConfirmModal.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 { useCheckDisableMouseover } from '@/composables/macCssFix.js'
|
||||||
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
|
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
|
||||||
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
|
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
|
||||||
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
|
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
|
||||||
import { useInstall } from '@/store/install.js'
|
import { useInstall } from '@/store/install.js'
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
import { open } from '@tauri-apps/plugin-shell'
|
||||||
import { get_opening_command, initialize_state } from '@/helpers/state'
|
import { get_opening_command, initialize_state } from '@/helpers/state'
|
||||||
|
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
|
||||||
|
import { renderString } from '@modrinth/utils'
|
||||||
|
import { useFetch } from '@/helpers/fetch.js'
|
||||||
|
import { check } from '@tauri-apps/plugin-updater'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
|
|
||||||
@@ -57,6 +63,12 @@ const os = ref('')
|
|||||||
|
|
||||||
const stateInitialized = ref(false)
|
const stateInitialized = ref(false)
|
||||||
|
|
||||||
|
const criticalErrorMessage = ref()
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await useCheckDisableMouseover()
|
||||||
|
})
|
||||||
|
|
||||||
async function setupApp() {
|
async function setupApp() {
|
||||||
stateInitialized.value = true
|
stateInitialized.value = true
|
||||||
const {
|
const {
|
||||||
@@ -79,21 +91,23 @@ async function setupApp() {
|
|||||||
showOnboarding.value = !onboarded
|
showOnboarding.value = !onboarded
|
||||||
|
|
||||||
nativeDecorations.value = native_decorations
|
nativeDecorations.value = native_decorations
|
||||||
if (os.value !== 'MacOS') await appWindow.setDecorations(native_decorations)
|
if (os.value !== 'MacOS') await getCurrentWindow().setDecorations(native_decorations)
|
||||||
|
|
||||||
themeStore.setThemeState(theme)
|
themeStore.setThemeState(theme)
|
||||||
themeStore.collapsedNavigation = collapsed_navigation
|
themeStore.collapsedNavigation = collapsed_navigation
|
||||||
themeStore.advancedRendering = advanced_rendering
|
themeStore.advancedRendering = advanced_rendering
|
||||||
|
|
||||||
mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
|
initAnalytics()
|
||||||
if (!telemetry) {
|
if (!telemetry) {
|
||||||
mixpanel_opt_out_tracking()
|
optOutAnalytics()
|
||||||
}
|
}
|
||||||
mixpanel_track('Launched', { version, dev, onboarded })
|
if (dev) debugAnalytics()
|
||||||
|
trackEvent('Launched', { version, dev, onboarded })
|
||||||
|
|
||||||
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
||||||
|
|
||||||
if ((await type()) === 'Darwin') {
|
const osType = await type()
|
||||||
|
if (osType === 'macos') {
|
||||||
document.getElementsByTagName('html')[0].classList.add('mac')
|
document.getElementsByTagName('html')[0].classList.add('mac')
|
||||||
} else {
|
} else {
|
||||||
document.getElementsByTagName('html')[0].classList.add('windows')
|
document.getElementsByTagName('html')[0].classList.add('windows')
|
||||||
@@ -107,7 +121,18 @@ async function setupApp() {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useFetch(
|
||||||
|
`https://api.modrinth.com/appCriticalAnnouncement.json?version=${version}`,
|
||||||
|
'criticalAnnouncements',
|
||||||
|
true,
|
||||||
|
).then((res) => {
|
||||||
|
if (res && res.header && res.body) {
|
||||||
|
criticalErrorMessage.value = res
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
get_opening_command().then(handleCommand)
|
get_opening_command().then(handleCommand)
|
||||||
|
checkUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateFailed = ref(false)
|
const stateFailed = ref(false)
|
||||||
@@ -127,18 +152,12 @@ initialize_state()
|
|||||||
|
|
||||||
const handleClose = async () => {
|
const handleClose = async () => {
|
||||||
await saveWindowState(StateFlags.ALL)
|
await saveWindowState(StateFlags.ALL)
|
||||||
await TauriWindow.getCurrent().close()
|
await getCurrentWindow().close()
|
||||||
}
|
}
|
||||||
|
|
||||||
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
|
||||||
await handleClose()
|
|
||||||
})
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
router.afterEach((to, from, failure) => {
|
router.afterEach((to, from, failure) => {
|
||||||
if (mixpanel_is_loaded()) {
|
trackEvent('PageView', { path: to.path, fromPath: from.path, failed: failure })
|
||||||
mixpanel_track('PageView', { path: to.path, fromPath: from.path, failed: failure })
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const isOnBrowse = computed(() => route.path.startsWith('/browse'))
|
const isOnBrowse = computed(() => route.path.startsWith('/browse'))
|
||||||
@@ -178,15 +197,10 @@ document.querySelector('body').addEventListener('click', function (e) {
|
|||||||
['http://', 'https://', 'mailto:', 'tel:'].some((v) => target.href.startsWith(v)) &&
|
['http://', 'https://', 'mailto:', 'tel:'].some((v) => target.href.startsWith(v)) &&
|
||||||
!target.classList.contains('router-link-active') &&
|
!target.classList.contains('router-link-active') &&
|
||||||
!target.href.startsWith('http://localhost') &&
|
!target.href.startsWith('http://localhost') &&
|
||||||
!target.href.startsWith('https://tauri.localhost')
|
!target.href.startsWith('https://tauri.localhost') &&
|
||||||
|
!target.href.startsWith('http://tauri.localhost')
|
||||||
) {
|
) {
|
||||||
window.__TAURI_INVOKE__('tauri', {
|
open(target.href)
|
||||||
__tauriModule: 'Shell',
|
|
||||||
message: {
|
|
||||||
cmd: 'open',
|
|
||||||
path: target.href,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
break
|
break
|
||||||
@@ -219,7 +233,7 @@ async function handleCommand(e) {
|
|||||||
// 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')) {
|
||||||
await install_from_file(e.path).catch(handleError)
|
await install_from_file(e.path).catch(handleError)
|
||||||
mixpanel_track('InstanceCreate', {
|
trackEvent('InstanceCreate', {
|
||||||
source: 'CreationModalFileDrop',
|
source: 'CreationModalFileDrop',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -228,6 +242,20 @@ async function handleCommand(e) {
|
|||||||
urlModal.value.show(e)
|
urlModal.value.show(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateAvailable = ref(false)
|
||||||
|
async function checkUpdates() {
|
||||||
|
const update = await check()
|
||||||
|
console.log(update)
|
||||||
|
updateAvailable.value = !!update
|
||||||
|
|
||||||
|
setTimeout(
|
||||||
|
() => {
|
||||||
|
checkUpdates()
|
||||||
|
},
|
||||||
|
5 * 1000 * 60,
|
||||||
|
)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -261,6 +289,14 @@ async function handleCommand(e) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings pages-list">
|
<div class="settings pages-list">
|
||||||
|
<button
|
||||||
|
v-if="updateAvailable"
|
||||||
|
v-tooltip="'Install update'"
|
||||||
|
class="btn btn-outline btn-primary icon-only collapsed-button"
|
||||||
|
@click="restartApp()"
|
||||||
|
>
|
||||||
|
<DownloadIcon />
|
||||||
|
</button>
|
||||||
<Button
|
<Button
|
||||||
v-tooltip="'Create profile'"
|
v-tooltip="'Create profile'"
|
||||||
class="sleek-primary collapsed-button"
|
class="sleek-primary collapsed-button"
|
||||||
@@ -276,6 +312,10 @@ async function handleCommand(e) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="view">
|
<div class="view">
|
||||||
|
<div v-if="criticalErrorMessage" class="critical-error-banner" data-tauri-drag-region>
|
||||||
|
<h1>{{ criticalErrorMessage.header }}</h1>
|
||||||
|
<div class="markdown-body" v-html="renderString(criticalErrorMessage.body ?? '')"></div>
|
||||||
|
</div>
|
||||||
<div class="appbar-row">
|
<div class="appbar-row">
|
||||||
<div data-tauri-drag-region class="appbar">
|
<div data-tauri-drag-region class="appbar">
|
||||||
<section class="navigation-controls">
|
<section class="navigation-controls">
|
||||||
@@ -288,10 +328,14 @@ async function handleCommand(e) {
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<section v-if="!nativeDecorations" class="window-controls">
|
<section v-if="!nativeDecorations" class="window-controls">
|
||||||
<Button class="titlebar-button" icon-only @click="() => appWindow.minimize()">
|
<Button class="titlebar-button" icon-only @click="() => getCurrentWindow().minimize()">
|
||||||
<MinimizeIcon />
|
<MinimizeIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="titlebar-button" icon-only @click="() => appWindow.toggleMaximize()">
|
<Button
|
||||||
|
class="titlebar-button"
|
||||||
|
icon-only
|
||||||
|
@click="() => getCurrentWindow().toggleMaximize()"
|
||||||
|
>
|
||||||
<MaximizeIcon />
|
<MaximizeIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="titlebar-button close" icon-only @click="handleClose">
|
<Button class="titlebar-button close" icon-only @click="handleClose">
|
||||||
@@ -384,6 +428,16 @@ async function handleCommand(e) {
|
|||||||
width: calc(100% - var(--sidebar-width));
|
width: calc(100% - var(--sidebar-width));
|
||||||
background-color: var(--color-raised-bg);
|
background-color: var(--color-raised-bg);
|
||||||
|
|
||||||
|
.critical-error-banner {
|
||||||
|
margin-top: -1.25rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: rgba(203, 34, 69, 0.1);
|
||||||
|
border-left: 2px solid var(--color-red);
|
||||||
|
border-bottom: 2px solid var(--color-red);
|
||||||
|
border-right: 2px solid var(--color-red);
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.appbar {
|
.appbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ import {
|
|||||||
SearchIcon,
|
SearchIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { ConfirmModal, Button, Card, DropdownSelect } from '@modrinth/ui'
|
import { Button, Card, DropdownSelect } from '@modrinth/ui'
|
||||||
import { formatCategoryHeader } from '@modrinth/utils'
|
import { formatCategoryHeader } from '@modrinth/utils'
|
||||||
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 { duplicate, remove } from '@/helpers/profile.js'
|
import { duplicate, remove } from '@/helpers/profile.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
|
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instances: {
|
instances: {
|
||||||
@@ -35,7 +35,6 @@ const props = defineProps({
|
|||||||
const instanceOptions = ref(null)
|
const instanceOptions = ref(null)
|
||||||
const instanceComponents = ref(null)
|
const instanceComponents = ref(null)
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
const currentDeleteInstance = ref(null)
|
const currentDeleteInstance = ref(null)
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
|
|
||||||
@@ -230,13 +229,12 @@ const filteredResults = computed(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModalWrapper
|
||||||
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."
|
||||||
:has-to-type="false"
|
:has-to-type="false"
|
||||||
proceed-label="Delete"
|
proceed-label="Delete"
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
@proceed="deleteProfile"
|
@proceed="deleteProfile"
|
||||||
/>
|
/>
|
||||||
<Card class="header">
|
<Card class="header">
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
EyeIcon,
|
EyeIcon,
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { ConfirmModal } from '@modrinth/ui'
|
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
|
||||||
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'
|
||||||
@@ -22,8 +22,7 @@ import { handleError } from '@/store/notifications.js'
|
|||||||
import { duplicate, kill, 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 { useTheming } from '@/store/state.js'
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
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'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
|
|
||||||
@@ -53,7 +52,6 @@ const instanceComponents = ref(null)
|
|||||||
const rows = ref(null)
|
const rows = ref(null)
|
||||||
const deleteConfirmModal = ref(null)
|
const deleteConfirmModal = ref(null)
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
const currentDeleteInstance = ref(null)
|
const currentDeleteInstance = ref(null)
|
||||||
|
|
||||||
async function deleteProfile() {
|
async function deleteProfile() {
|
||||||
@@ -125,14 +123,14 @@ const handleOptionsClick = async (args) => {
|
|||||||
await run(args.item.path).catch((err) =>
|
await run(args.item.path).catch((err) =>
|
||||||
handleSevereError(err, { profilePath: args.item.path }),
|
handleSevereError(err, { profilePath: args.item.path }),
|
||||||
)
|
)
|
||||||
mixpanel_track('InstanceStart', {
|
trackEvent('InstanceStart', {
|
||||||
loader: args.item.loader,
|
loader: args.item.loader,
|
||||||
game_version: args.item.game_version,
|
game_version: args.item.game_version,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'stop':
|
case 'stop':
|
||||||
await kill(args.item.path).catch(handleError)
|
await kill(args.item.path).catch(handleError)
|
||||||
mixpanel_track('InstanceStop', {
|
trackEvent('InstanceStop', {
|
||||||
loader: args.item.loader,
|
loader: args.item.loader,
|
||||||
game_version: args.item.game_version,
|
game_version: args.item.game_version,
|
||||||
})
|
})
|
||||||
@@ -207,13 +205,12 @@ onUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModalWrapper
|
||||||
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."
|
||||||
:has-to-type="false"
|
:has-to-type="false"
|
||||||
proceed-label="Delete"
|
proceed-label="Delete"
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
@proceed="deleteProfile"
|
@proceed="deleteProfile"
|
||||||
/>
|
/>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
v-tooltip.right="'Minecraft accounts'"
|
v-tooltip.right="'Minecraft accounts'"
|
||||||
class="button-base avatar-button"
|
class="button-base avatar-button"
|
||||||
:class="{ expanded: mode === 'expanded' }"
|
:class="{ expanded: mode === 'expanded' }"
|
||||||
@click="showCard = !showCard"
|
@click="toggleMenu"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
:size="mode === 'expanded' ? 'xs' : 'sm'"
|
:size="mode === 'expanded' ? 'xs' : 'sm'"
|
||||||
@@ -70,9 +70,10 @@ import {
|
|||||||
get_default_user,
|
get_default_user,
|
||||||
} from '@/helpers/auth'
|
} from '@/helpers/auth'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
import { process_listener } from '@/helpers/events'
|
import { process_listener } from '@/helpers/events'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
|
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
mode: {
|
mode: {
|
||||||
@@ -118,7 +119,7 @@ async function login() {
|
|||||||
await refreshValues()
|
await refreshValues()
|
||||||
}
|
}
|
||||||
|
|
||||||
mixpanel_track('AccountLogIn')
|
trackEvent('AccountLogIn')
|
||||||
}
|
}
|
||||||
|
|
||||||
const logout = async (id) => {
|
const logout = async (id) => {
|
||||||
@@ -130,12 +131,12 @@ const logout = async (id) => {
|
|||||||
} else {
|
} else {
|
||||||
emit('change')
|
emit('change')
|
||||||
}
|
}
|
||||||
mixpanel_track('AccountLogOut')
|
trackEvent('AccountLogOut')
|
||||||
}
|
}
|
||||||
|
|
||||||
let showCard = ref(false)
|
const showCard = ref(false)
|
||||||
let card = ref(null)
|
const card = ref(null)
|
||||||
let button = ref(null)
|
const button = ref(null)
|
||||||
const handleClickOutside = (event) => {
|
const handleClickOutside = (event) => {
|
||||||
const elements = document.elementsFromPoint(event.clientX, event.clientY)
|
const elements = document.elementsFromPoint(event.clientX, event.clientY)
|
||||||
if (
|
if (
|
||||||
@@ -144,7 +145,20 @@ const handleClickOutside = (event) => {
|
|||||||
!elements.includes(card.value.$el) &&
|
!elements.includes(card.value.$el) &&
|
||||||
!button.value.contains(event.target)
|
!button.value.contains(event.target)
|
||||||
) {
|
) {
|
||||||
|
toggleMenu(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMenu(override = true) {
|
||||||
|
if (showCard.value || !override) {
|
||||||
|
if (showCard.value) {
|
||||||
|
show_ads_window()
|
||||||
|
}
|
||||||
|
|
||||||
showCard.value = false
|
showCard.value = false
|
||||||
|
} else {
|
||||||
|
hide_ads_window()
|
||||||
|
showCard.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DropdownIcon, FolderOpenIcon, SearchIcon } from '@modrinth/assets'
|
import { DropdownIcon, FolderOpenIcon, SearchIcon } from '@modrinth/assets'
|
||||||
import { Button, OverflowMenu } from '@modrinth/ui'
|
import { Button, OverflowMenu } from '@modrinth/ui'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/plugin-dialog'
|
||||||
import { add_project_from_path } from '@/helpers/profile.js'
|
import { add_project_from_path } from '@/helpers/profile.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@@ -20,7 +20,7 @@ const handleAddContentFromFile = async () => {
|
|||||||
if (!newProject) return
|
if (!newProject) return
|
||||||
|
|
||||||
for (const project of newProject) {
|
for (const project of newProject) {
|
||||||
await add_project_from_path(props.instance.path, project).catch(handleError)
|
await add_project_from_path(props.instance.path, project.path ?? project).catch(handleError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
|
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
||||||
|
|
||||||
const emit = defineEmits(['menu-closed', 'option-clicked'])
|
const emit = defineEmits(['menu-closed', 'option-clicked'])
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ const shown = ref(false)
|
|||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
showMenu: (event, passedItem, passedOptions) => {
|
showMenu: (event, passedItem, passedOptions) => {
|
||||||
|
hide_ads_window()
|
||||||
item.value = passedItem
|
item.value = passedItem
|
||||||
options.value = passedOptions
|
options.value = passedOptions
|
||||||
|
|
||||||
@@ -69,6 +71,9 @@ const isLinkedData = (item) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hideContextMenu = () => {
|
const hideContextMenu = () => {
|
||||||
|
if (shown.value) {
|
||||||
|
show_ads_window()
|
||||||
|
}
|
||||||
shown.value = false
|
shown.value = false
|
||||||
emit('menu-closed')
|
emit('menu-closed')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { XIcon, HammerIcon, LogInIcon, UpdatedIcon } from '@modrinth/assets'
|
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 { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
import { cancel_directory_change } from '@/helpers/settings.js'
|
import { cancel_directory_change } from '@/helpers/settings.js'
|
||||||
import { install } from '@/helpers/profile.js'
|
import { install } from '@/helpers/profile.js'
|
||||||
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
|
|
||||||
const errorModal = ref()
|
const errorModal = ref()
|
||||||
const error = ref()
|
const error = ref()
|
||||||
@@ -85,7 +85,7 @@ async function loginMinecraft() {
|
|||||||
await set_default_user(loggedIn.id).catch(handleError)
|
await set_default_user(loggedIn.id).catch(handleError)
|
||||||
}
|
}
|
||||||
|
|
||||||
await mixpanel.track('AccountLogIn')
|
await trackEvent('AccountLogIn', { source: 'ErrorModal' })
|
||||||
loadingMinecraft.value = false
|
loadingMinecraft.value = false
|
||||||
errorModal.value.hide()
|
errorModal.value.hide()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -121,7 +121,7 @@ async function repairInstance() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal ref="errorModal" :header="title" :closable="closable">
|
<ModalWrapper 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'">
|
||||||
@@ -230,7 +230,7 @@ async function repairInstance() {
|
|||||||
</p>
|
</p>
|
||||||
<p>You may be able to fix it through one of the following ways:</p>
|
<p>You may be able to fix it through one of the following ways:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Ennsuring you are connected to the internet, then try restarting the app.</li>
|
<li>Ensuring you are connected to the internet, then try restarting the app.</li>
|
||||||
<li>Redownloading the app.</li>
|
<li>Redownloading the app.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
@@ -272,7 +272,7 @@ async function repairInstance() {
|
|||||||
<button v-if="closable" class="btn" @click="errorModal.hide()"><XIcon /> Close</button>
|
<button v-if="closable" class="btn" @click="errorModal.hide()"><XIcon /> Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { XIcon, PlusIcon } from '@modrinth/assets'
|
import { XIcon, PlusIcon } from '@modrinth/assets'
|
||||||
import { Button, Checkbox, Modal } from '@modrinth/ui'
|
import { Button, Checkbox } 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'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/plugin-dialog'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { useTheming } from '@/store/theme'
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instance: {
|
instance: {
|
||||||
@@ -30,8 +30,6 @@ const files = ref([])
|
|||||||
const folders = ref([])
|
const folders = ref([])
|
||||||
const showingFiles = ref(false)
|
const showingFiles = ref(false)
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
const initFiles = async () => {
|
const initFiles = async () => {
|
||||||
const newFolders = new Map()
|
const newFolders = new Map()
|
||||||
const sep = '/'
|
const sep = '/'
|
||||||
@@ -106,7 +104,7 @@ const exportPack = async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal ref="exportModal" header="Export modpack" :noblur="!themeStore.advancedRendering">
|
<ModalWrapper ref="exportModal" header="Export modpack">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="labeled_input">
|
<div class="labeled_input">
|
||||||
<p>Modpack Name</p>
|
<p>Modpack Name</p>
|
||||||
@@ -208,7 +206,7 @@ const exportPack = async () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { onUnmounted, ref, computed } from 'vue'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { StopCircleIcon, PlayIcon } from '@modrinth/assets'
|
import { StopCircleIcon, PlayIcon } from '@modrinth/assets'
|
||||||
import { Card, Avatar, AnimatedLogo } from '@modrinth/ui'
|
import { Card, Avatar, AnimatedLogo } from '@modrinth/ui'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||||
import { kill, run } from '@/helpers/profile'
|
import { kill, run } from '@/helpers/profile'
|
||||||
import { get_by_profile_path } from '@/helpers/process'
|
import { get_by_profile_path } from '@/helpers/process'
|
||||||
import { process_listener } from '@/helpers/events'
|
import { process_listener } from '@/helpers/events'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instance: {
|
instance: {
|
||||||
@@ -45,7 +45,7 @@ const play = async (e, context) => {
|
|||||||
)
|
)
|
||||||
modLoading.value = false
|
modLoading.value = false
|
||||||
|
|
||||||
mixpanel_track('InstancePlay', {
|
trackEvent('InstancePlay', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
@@ -58,7 +58,7 @@ const stop = async (e, context) => {
|
|||||||
|
|
||||||
await kill(props.instance.path).catch(handleError)
|
await kill(props.instance.path).catch(handleError)
|
||||||
|
|
||||||
mixpanel_track('InstanceStop', {
|
trackEvent('InstanceStop', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Modal ref="modal" header="Create instance" :noblur="!themeStore.advancedRendering">
|
<ModalWrapper ref="modal" header="Create instance">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<Chips v-model="creationType" :items="['custom', 'from file', 'import from launcher']" />
|
<Chips v-model="creationType" :items="['custom', 'from file', 'import from launcher']" />
|
||||||
</div>
|
</div>
|
||||||
@@ -193,10 +193,11 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
import {
|
import {
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
UploadIcon,
|
UploadIcon,
|
||||||
@@ -207,18 +208,16 @@ import {
|
|||||||
FolderSearchIcon,
|
FolderSearchIcon,
|
||||||
UpdatedIcon,
|
UpdatedIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Avatar, Button, Chips, Modal, Checkbox } from '@modrinth/ui'
|
import { Avatar, Button, Chips, 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/plugin-dialog'
|
||||||
import { tauri } from '@tauri-apps/api'
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||||
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
|
import { get_game_versions, get_loader_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 { trackEvent } from '@/helpers/analytics'
|
||||||
import { useTheming } from '@/store/state.js'
|
|
||||||
import { listen } from '@tauri-apps/api/event'
|
|
||||||
import { install_from_file } from '@/helpers/pack.js'
|
import { install_from_file } from '@/helpers/pack.js'
|
||||||
import {
|
import {
|
||||||
get_default_launcher_path,
|
get_default_launcher_path,
|
||||||
@@ -226,8 +225,7 @@ import {
|
|||||||
import_instance,
|
import_instance,
|
||||||
} from '@/helpers/import.js'
|
} from '@/helpers/import.js'
|
||||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||||
|
import { getCurrentWebview } from '@tauri-apps/api/webview'
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
const profile_name = ref('')
|
const profile_name = ref('')
|
||||||
const game_version = ref('')
|
const game_version = ref('')
|
||||||
@@ -257,20 +255,22 @@ defineExpose({
|
|||||||
isShowing.value = true
|
isShowing.value = true
|
||||||
modal.value.show()
|
modal.value.show()
|
||||||
|
|
||||||
unlistener.value = await listen('tauri://file-drop', async (event) => {
|
unlistener.value = await getCurrentWebview().onDragDropEvent(async (event) => {
|
||||||
// Only if modal is showing
|
// Only if modal is showing
|
||||||
if (!isShowing.value) return
|
if (!isShowing.value) return
|
||||||
|
if (event.payload.type !== 'drop') return
|
||||||
if (creationType.value !== 'from file') return
|
if (creationType.value !== 'from file') return
|
||||||
hide()
|
hide()
|
||||||
if (event.payload && event.payload.length > 0 && event.payload[0].endsWith('.mrpack')) {
|
const { paths } = event.payload
|
||||||
await install_from_file(event.payload[0]).catch(handleError)
|
if (paths && paths.length > 0 && paths[0].endsWith('.mrpack')) {
|
||||||
mixpanel_track('InstanceCreate', {
|
await install_from_file(paths[0]).catch(handleError)
|
||||||
|
trackEvent('InstanceCreate', {
|
||||||
source: 'CreationModalFileDrop',
|
source: 'CreationModalFileDrop',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
mixpanel_track('InstanceCreateStart', { source: 'CreationModal' })
|
trackEvent('InstanceCreateStart', { source: 'CreationModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -360,7 +360,7 @@ const create_instance = async () => {
|
|||||||
icon.value,
|
icon.value,
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
|
|
||||||
mixpanel_track('InstanceCreate', {
|
trackEvent('InstanceCreate', {
|
||||||
profile_name: profile_name.value,
|
profile_name: profile_name.value,
|
||||||
game_version: game_version.value,
|
game_version: game_version.value,
|
||||||
loader: loader.value,
|
loader: loader.value,
|
||||||
@@ -371,7 +371,7 @@ const create_instance = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const upload_icon = async () => {
|
const upload_icon = async () => {
|
||||||
icon.value = await open({
|
const res = await open({
|
||||||
multiple: false,
|
multiple: false,
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
@@ -381,8 +381,10 @@ const upload_icon = async () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
icon.value = res.path ?? res
|
||||||
|
|
||||||
if (!icon.value) return
|
if (!icon.value) return
|
||||||
display_icon.value = tauri.convertFileSrc(icon.value)
|
display_icon.value = convertFileSrc(icon.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const reset_icon = () => {
|
const reset_icon = () => {
|
||||||
@@ -417,9 +419,9 @@ const openFile = async () => {
|
|||||||
const newProject = await open({ multiple: false })
|
const newProject = await open({ multiple: false })
|
||||||
if (!newProject) return
|
if (!newProject) return
|
||||||
hide()
|
hide()
|
||||||
await install_from_file(newProject).catch(handleError)
|
await install_from_file(newProject.path ?? newProject).catch(handleError)
|
||||||
|
|
||||||
mixpanel_track('InstanceCreate', {
|
trackEvent('InstanceCreate', {
|
||||||
source: 'CreationModalFileOpen',
|
source: 'CreationModalFileOpen',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -462,7 +464,7 @@ const promises = profileOptions.value.map(async (option) => {
|
|||||||
option.name,
|
option.name,
|
||||||
instances.map((name) => ({ name, selected: false })),
|
instances.map((name) => ({ name, selected: false })),
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch {
|
||||||
// Allow failure silently
|
// Allow failure silently
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Modal ref="detectJavaModal" header="Select java version" :noblur="!themeStore.advancedRendering">
|
<ModalWrapper ref="detectJavaModal" header="Select java version">
|
||||||
<div class="auto-detect-modal">
|
<div class="auto-detect-modal">
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<div class="table-row table-head">
|
<div class="table-row table-head">
|
||||||
@@ -32,18 +32,16 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { PlusIcon, CheckIcon, XIcon } from '@modrinth/assets'
|
import { PlusIcon, CheckIcon, XIcon } from '@modrinth/assets'
|
||||||
import { Modal, Button } from '@modrinth/ui'
|
import { 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'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
import { useTheming } from '@/store/theme.js'
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
const chosenInstallOptions = ref([])
|
const chosenInstallOptions = ref([])
|
||||||
const detectJavaModal = ref(null)
|
const detectJavaModal = ref(null)
|
||||||
@@ -67,7 +65,7 @@ const emit = defineEmits(['submit'])
|
|||||||
function setJavaInstall(javaInstall) {
|
function setJavaInstall(javaInstall) {
|
||||||
emit('submit', javaInstall)
|
emit('submit', javaInstall)
|
||||||
detectJavaModal.value.hide()
|
detectJavaModal.value.hide()
|
||||||
mixpanel_track('JavaAutoDetect', {
|
trackEvent('JavaAutoDetect', {
|
||||||
path: javaInstall.path,
|
path: javaInstall.path,
|
||||||
version: javaInstall.version,
|
version: javaInstall.version,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ import {
|
|||||||
import { Button } from '@modrinth/ui'
|
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/plugin-dialog'
|
||||||
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
|
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
version: {
|
version: {
|
||||||
@@ -113,7 +113,7 @@ async function testJava() {
|
|||||||
)
|
)
|
||||||
testingJava.value = false
|
testingJava.value = false
|
||||||
|
|
||||||
mixpanel_track('JavaTest', {
|
trackEvent('JavaTest', {
|
||||||
path: props.modelValue ? props.modelValue.path : '',
|
path: props.modelValue ? props.modelValue.path : '',
|
||||||
success: testingJavaSuccess.value,
|
success: testingJavaSuccess.value,
|
||||||
})
|
})
|
||||||
@@ -124,20 +124,19 @@ async function testJava() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleJavaFileInput() {
|
async function handleJavaFileInput() {
|
||||||
let filePath = await open()
|
const filePath = await open()
|
||||||
|
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
let result = await get_jre(filePath)
|
let result = await get_jre(filePath.path ?? filePath)
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = {
|
result = {
|
||||||
path: filePath,
|
path: filePath.path ?? filePath,
|
||||||
version: props.version.toString(),
|
version: props.version.toString(),
|
||||||
architecture: 'x86',
|
architecture: 'x86',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mixpanel_track('JavaManualSelect', {
|
trackEvent('JavaManualSelect', {
|
||||||
path: filePath,
|
|
||||||
version: props.version,
|
version: props.version,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -150,7 +149,7 @@ async function autoDetect() {
|
|||||||
if (!props.compact) {
|
if (!props.compact) {
|
||||||
detectJavaModal.value.show(props.version, props.modelValue)
|
detectJavaModal.value.show(props.version, props.modelValue)
|
||||||
} else {
|
} else {
|
||||||
let versions = await find_filtered_jres(props.version).catch(handleError)
|
const versions = await find_filtered_jres(props.version).catch(handleError)
|
||||||
if (versions.length > 0) {
|
if (versions.length > 0) {
|
||||||
emit('update:modelValue', versions[0])
|
emit('update:modelValue', versions[0])
|
||||||
}
|
}
|
||||||
@@ -170,7 +169,7 @@ async function reinstallJava() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mixpanel_track('JavaReInstall', {
|
trackEvent('JavaReInstall', {
|
||||||
path: path,
|
path: path,
|
||||||
version: props.version,
|
version: props.version,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { CheckIcon } from '@modrinth/assets'
|
import { CheckIcon } from '@modrinth/assets'
|
||||||
import { Button, Modal, Badge } from '@modrinth/ui'
|
import { Button, Badge } from '@modrinth/ui'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useTheming } from '@/store/theme'
|
|
||||||
import { update_managed_modrinth_version } from '@/helpers/profile'
|
import { update_managed_modrinth_version } from '@/helpers/profile'
|
||||||
import { releaseColor } from '@/helpers/utils'
|
import { releaseColor } from '@/helpers/utils'
|
||||||
import { SwapIcon } from '@/assets/icons/index.js'
|
import { SwapIcon } from '@/assets/icons/index.js'
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
versions: {
|
versions: {
|
||||||
@@ -33,8 +33,6 @@ 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)
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
const switchVersion = async (versionId) => {
|
const switchVersion = async (versionId) => {
|
||||||
inProgress.value = true
|
inProgress.value = true
|
||||||
await update_managed_modrinth_version(props.instance.path, versionId)
|
await update_managed_modrinth_version(props.instance.path, versionId)
|
||||||
@@ -43,11 +41,10 @@ const switchVersion = async (versionId) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<ModalWrapper
|
||||||
ref="modpackVersionModal"
|
ref="modpackVersionModal"
|
||||||
class="modpack-version-modal"
|
class="modpack-version-modal"
|
||||||
header="Change modpack version"
|
header="Change modpack version"
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<Card v-if="instance.linked_data" class="mod-card">
|
<Card v-if="instance.linked_data" class="mod-card">
|
||||||
@@ -111,7 +108,7 @@ const switchVersion = async (versionId) => {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
import { Promotion } from '@modrinth/ui'
|
|
||||||
import { get as getCreds } from '@/helpers/mr_auth.js'
|
import { get as getCreds } from '@/helpers/mr_auth.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { get_user } from '@/helpers/cache.js'
|
import { get_user } from '@/helpers/cache.js'
|
||||||
|
import { ChevronRightIcon } from '@modrinth/assets'
|
||||||
|
import { init_ads_window, open_ads_link, record_ads_click } from '@/helpers/ads.js'
|
||||||
|
import { listen } from '@tauri-apps/api/event'
|
||||||
|
|
||||||
const showAd = ref(true)
|
const showAd = ref(true)
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
scroll() {
|
||||||
|
updateAdPosition()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const creds = await getCreds().catch(handleError)
|
const creds = await getCreds().catch(handleError)
|
||||||
if (creds && creds.user_id) {
|
if (creds && creds.user_id) {
|
||||||
const user = await get_user(creds.user_id).catch(handleError)
|
const user = await get_user(creds.user_id).catch(handleError)
|
||||||
@@ -16,8 +24,103 @@ if (creds && creds.user_id) {
|
|||||||
showAd.value = false
|
showAd.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const adsWrapper = ref(null)
|
||||||
|
let resizeObserver
|
||||||
|
let scrollHandler
|
||||||
|
let intersectionObserver
|
||||||
|
let mutationObserver
|
||||||
|
onMounted(() => {
|
||||||
|
if (showAd.value) {
|
||||||
|
updateAdPosition(true)
|
||||||
|
|
||||||
|
resizeObserver = new ResizeObserver(() => updateAdPosition())
|
||||||
|
resizeObserver.observe(adsWrapper.value)
|
||||||
|
|
||||||
|
intersectionObserver = new IntersectionObserver(() => updateAdPosition())
|
||||||
|
intersectionObserver.observe(adsWrapper.value)
|
||||||
|
|
||||||
|
mutationObserver = new MutationObserver(() => updateAdPosition())
|
||||||
|
mutationObserver.observe(adsWrapper.value, { attributes: true, childList: true, subtree: true })
|
||||||
|
|
||||||
|
// Add scroll event listener
|
||||||
|
scrollHandler = () => {
|
||||||
|
requestAnimationFrame(() => updateAdPosition())
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', scrollHandler, { passive: true })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function updateAdPosition(overrideShown = false) {
|
||||||
|
if (adsWrapper.value) {
|
||||||
|
const rect = adsWrapper.value.getBoundingClientRect()
|
||||||
|
|
||||||
|
let y = rect.top + window.scrollY
|
||||||
|
let height = rect.bottom - rect.top
|
||||||
|
|
||||||
|
// Prevent ad from overlaying the app bar
|
||||||
|
if (y <= 52) {
|
||||||
|
y = 52
|
||||||
|
height = rect.bottom - 52
|
||||||
|
|
||||||
|
if (height < 0) {
|
||||||
|
height = 0
|
||||||
|
y = -1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init_ads_window(rect.left + window.scrollX, y, rect.right - rect.left, height, overrideShown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openPlusLink() {
|
||||||
|
await record_ads_click()
|
||||||
|
await open_ads_link('https://modrinth.com/plus', 'https://modrinth.com')
|
||||||
|
}
|
||||||
|
|
||||||
|
const unlisten = await listen('ads-scroll', (event) => {
|
||||||
|
if (adsWrapper.value) {
|
||||||
|
adsWrapper.value.parentNode.scrollTop += event.payload.scroll
|
||||||
|
updateAdPosition()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (resizeObserver) {
|
||||||
|
resizeObserver.disconnect()
|
||||||
|
}
|
||||||
|
if (intersectionObserver) {
|
||||||
|
intersectionObserver.disconnect()
|
||||||
|
}
|
||||||
|
if (mutationObserver) {
|
||||||
|
mutationObserver.disconnect()
|
||||||
|
}
|
||||||
|
if (scrollHandler) {
|
||||||
|
window.removeEventListener('scroll', scrollHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
unlisten()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Promotion v-if="showAd" :external="false" query-param="?r=launcher" />
|
<div
|
||||||
|
v-if="showAd"
|
||||||
|
ref="adsWrapper"
|
||||||
|
class="ad-parent relative mb-3 flex w-full justify-center rounded-2xl bg-bg-raised cursor-pointer"
|
||||||
|
>
|
||||||
|
<div class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 p-6">
|
||||||
|
<p class="m-0 text-2xl font-bold text-contrast">75% of ad revenue goes to creators</p>
|
||||||
|
<button
|
||||||
|
class="mt-auto items-center gap-1 text-purple hover:underline bg-transparent border-none text-left cursor-pointer outline-none"
|
||||||
|
@click="openPlusLink"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
Support creators and Modrinth ad-free with
|
||||||
|
<span class="font-bold">Modrinth+</span>
|
||||||
|
</span>
|
||||||
|
<ChevronRightIcon class="relative top-[3px] h-5 w-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -117,9 +117,9 @@ import { useRouter } from 'vue-router'
|
|||||||
import { progress_bars_list } from '@/helpers/state.js'
|
import { progress_bars_list } from '@/helpers/state.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 { ChatIcon } from '@/assets/icons'
|
import { ChatIcon } from '@/assets/icons'
|
||||||
import { get_many } from '@/helpers/profile.js'
|
import { get_many } from '@/helpers/profile.js'
|
||||||
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const card = ref(null)
|
const card = ref(null)
|
||||||
@@ -164,7 +164,7 @@ const stop = async (process) => {
|
|||||||
try {
|
try {
|
||||||
await killProcess(process.uuid).catch(handleError)
|
await killProcess(process.uuid).catch(handleError)
|
||||||
|
|
||||||
mixpanel_track('InstanceStop', {
|
trackEvent('InstanceStop', {
|
||||||
loader: process.profile.loader,
|
loader: process.profile.loader,
|
||||||
game_version: process.profile.game_version,
|
game_version: process.profile.game_version,
|
||||||
source: 'AppBar',
|
source: 'AppBar',
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="!hidden" class="splash-screen dark" :class="{ 'fade-out': doneLoading }">
|
<div v-if="!hidden" class="splash-screen dark" :class="{ 'fade-out': doneLoading }">
|
||||||
<div v-if="os !== 'MacOS'" class="app-buttons">
|
<div v-if="os !== 'MacOS'" class="app-buttons">
|
||||||
<button class="btn icon-only transparent" icon-only @click="() => appWindow.minimize()">
|
<button class="btn icon-only transparent" icon-only @click="() => getCurrent().minimize()">
|
||||||
<MinimizeIcon />
|
<MinimizeIcon />
|
||||||
</button>
|
</button>
|
||||||
<button class="btn icon-only transparent" @click="() => appWindow.toggleMaximize()">
|
<button class="btn icon-only transparent" @click="() => getCurrent().toggleMaximize()">
|
||||||
<MaximizeIcon />
|
<MaximizeIcon />
|
||||||
</button>
|
</button>
|
||||||
<button class="btn icon-only transparent" @click="handleClose">
|
<button class="btn icon-only transparent" @click="handleClose">
|
||||||
@@ -85,12 +85,9 @@
|
|||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||||
import { loading_listener } from '@/helpers/events.js'
|
import { loading_listener } from '@/helpers/events.js'
|
||||||
import { appWindow } from '@tauri-apps/api/window'
|
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||||
import { XIcon } from '@modrinth/assets'
|
import { XIcon } from '@modrinth/assets'
|
||||||
import { MaximizeIcon, MinimizeIcon } from '@/assets/icons/index.js'
|
import { MaximizeIcon, MinimizeIcon } from '@/assets/icons/index.js'
|
||||||
import { window as TauriWindow } from '@tauri-apps/api'
|
|
||||||
import { TauriEvent } from '@tauri-apps/api/event'
|
|
||||||
import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
|
||||||
import { getOS } from '@/helpers/utils.js'
|
import { getOS } from '@/helpers/utils.js'
|
||||||
import { useLoading } from '@/store/loading.js'
|
import { useLoading } from '@/store/loading.js'
|
||||||
|
|
||||||
@@ -131,20 +128,22 @@ const os = ref('')
|
|||||||
getOS().then((x) => (os.value = x))
|
getOS().then((x) => (os.value = x))
|
||||||
|
|
||||||
loading_listener(async (e) => {
|
loading_listener(async (e) => {
|
||||||
|
console.log(e)
|
||||||
if (e.event.type === 'directory_move') {
|
if (e.event.type === 'directory_move') {
|
||||||
loadingProgress.value = 100 * (e.fraction ?? 1)
|
loadingProgress.value = 100 * (e.fraction ?? 1)
|
||||||
message.value = 'Updating app directory...'
|
message.value = 'Updating app directory...'
|
||||||
|
} else if (e.event.type === 'launcher_update') {
|
||||||
|
loadingProgress.value = 100 * (e.fraction ?? 1)
|
||||||
|
message.value = 'Updating Modrinth App...'
|
||||||
|
} else if (e.event.type === 'checking_for_updates') {
|
||||||
|
loadingProgress.value = 100 * (e.fraction ?? 1)
|
||||||
|
message.value = 'Checking for updates...'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleClose = async () => {
|
const handleClose = async () => {
|
||||||
await saveWindowState(StateFlags.ALL)
|
await getCurrentWindow().close()
|
||||||
await TauriWindow.getCurrent().close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
|
||||||
await handleClose()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Modal, Button } from '@modrinth/ui'
|
import { Button } from '@modrinth/ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
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 { get_version, get_project } from '@/helpers/cache.js'
|
import { get_version, get_project } from '@/helpers/cache.js'
|
||||||
import { install as installVersion } from '@/store/install.js'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
|
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const project = ref(null)
|
const project = ref(null)
|
||||||
@@ -41,7 +42,7 @@ async function install() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal ref="confirmModal" :header="`Install ${project?.title}`">
|
<ModalWrapper ref="confirmModal" :header="`Install ${project?.title}`">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<SearchCard
|
<SearchCard
|
||||||
:project="project"
|
:project="project"
|
||||||
@@ -60,7 +61,7 @@ async function install() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Modal
|
<ModalWrapper ref="incompatibleModal" header="Incompatibility warning" :on-hide="onInstall">
|
||||||
ref="incompatibleModal"
|
|
||||||
header="Incompatibility warning"
|
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
:on-hide="onInstall"
|
|
||||||
>
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>
|
<p>
|
||||||
This {{ versions?.length > 0 ? 'project' : 'version' }} is not compatible with the instance
|
This {{ versions?.length > 0 ? 'project' : 'version' }} is not compatible with the instance
|
||||||
@@ -51,19 +46,18 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
||||||
import { Button, Modal, DropdownSelect } from '@modrinth/ui'
|
import { Button, DropdownSelect } from '@modrinth/ui'
|
||||||
import { formatCategory } from '@modrinth/utils'
|
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 } from '@/store/state.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
const instance = ref(null)
|
const instance = ref(null)
|
||||||
const project = ref(null)
|
const project = ref(null)
|
||||||
@@ -72,7 +66,7 @@ const selectedVersion = ref(null)
|
|||||||
const incompatibleModal = ref(null)
|
const incompatibleModal = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
|
|
||||||
let onInstall = ref(() => {})
|
const onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: (instanceVal, projectVal, projectVersions, callback) => {
|
show: (instanceVal, projectVal, projectVersions, callback) => {
|
||||||
@@ -87,7 +81,7 @@ defineExpose({
|
|||||||
|
|
||||||
incompatibleModal.value.show()
|
incompatibleModal.value.show()
|
||||||
|
|
||||||
mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
trackEvent('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -98,7 +92,7 @@ const install = async () => {
|
|||||||
onInstall.value(selectedVersion.value.id)
|
onInstall.value(selectedVersion.value.id)
|
||||||
incompatibleModal.value.hide()
|
incompatibleModal.value.hide()
|
||||||
|
|
||||||
mixpanel_track('ProjectInstall', {
|
trackEvent('ProjectInstall', {
|
||||||
loader: instance.value.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.game_version,
|
game_version: instance.value.game_version,
|
||||||
id: project.value,
|
id: project.value,
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
||||||
import { Button, Modal } from '@modrinth/ui'
|
import { Button } 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 { trackEvent } from '@/helpers/analytics'
|
||||||
import { useTheming } from '@/store/theme.js'
|
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
const versionId = ref()
|
const versionId = ref()
|
||||||
const project = ref()
|
const project = ref()
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
|
|
||||||
let onInstall = ref(() => {})
|
const onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: (projectVal, versionIdVal, callback) => {
|
show: (projectVal, versionIdVal, callback) => {
|
||||||
@@ -25,7 +23,7 @@ defineExpose({
|
|||||||
|
|
||||||
onInstall.value = callback
|
onInstall.value = callback
|
||||||
|
|
||||||
mixpanel_track('PackInstallStart')
|
trackEvent('PackInstallStart')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -39,7 +37,7 @@ async function install() {
|
|||||||
project.value.title,
|
project.value.title,
|
||||||
project.value.icon_url,
|
project.value.icon_url,
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
mixpanel_track('PackInstall', {
|
trackEvent('PackInstall', {
|
||||||
id: project.value.id,
|
id: project.value.id,
|
||||||
version_id: versionId.value,
|
version_id: versionId.value,
|
||||||
title: project.value.title,
|
title: project.value.title,
|
||||||
@@ -52,12 +50,7 @@ async function install() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<ModalWrapper ref="confirmModal" header="Are you sure?" :on-hide="onInstall">
|
||||||
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">
|
||||||
@@ -67,7 +60,7 @@ async function install() {
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
RightArrowIcon,
|
RightArrowIcon,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Avatar, Modal, Button, Card } from '@modrinth/ui'
|
import { Avatar, 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,
|
||||||
@@ -16,15 +16,14 @@ import {
|
|||||||
list,
|
list,
|
||||||
create,
|
create,
|
||||||
} from '@/helpers/profile'
|
} from '@/helpers/profile'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/plugin-dialog'
|
||||||
import { installVersionDependencies } from '@/store/install.js'
|
import { installVersionDependencies } from '@/store/install.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
|
||||||
import { useTheming } from '@/store/theme.js'
|
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { tauri } from '@tauri-apps/api'
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||||
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const versions = ref()
|
const versions = ref()
|
||||||
@@ -49,7 +48,7 @@ const shownProfiles = computed(() =>
|
|||||||
return profile.name.toLowerCase().includes(searchFilter.value.toLowerCase())
|
return profile.name.toLowerCase().includes(searchFilter.value.toLowerCase())
|
||||||
})
|
})
|
||||||
.filter((profile) => {
|
.filter((profile) => {
|
||||||
let loaders = versions.value.flatMap((v) => v.loaders)
|
const loaders = versions.value.flatMap((v) => v.loaders)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
versions.value.flatMap((v) => v.game_versions).includes(profile.game_version) &&
|
versions.value.flatMap((v) => v.game_versions).includes(profile.game_version) &&
|
||||||
@@ -60,7 +59,7 @@ const shownProfiles = computed(() =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
let onInstall = ref(() => {})
|
const onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: async (projectVal, versionsVal, callback) => {
|
show: async (projectVal, versionsVal, callback) => {
|
||||||
@@ -78,7 +77,7 @@ defineExpose({
|
|||||||
onInstall.value = callback
|
onInstall.value = callback
|
||||||
|
|
||||||
const profilesVal = await list().catch(handleError)
|
const profilesVal = await list().catch(handleError)
|
||||||
for (let profile of profilesVal) {
|
for (const profile of profilesVal) {
|
||||||
profile.installing = false
|
profile.installing = false
|
||||||
profile.installedMod = await check_installed(profile.path, project.value.id).catch(
|
profile.installedMod = await check_installed(profile.path, project.value.id).catch(
|
||||||
handleError,
|
handleError,
|
||||||
@@ -88,7 +87,7 @@ defineExpose({
|
|||||||
|
|
||||||
installModal.value.show()
|
installModal.value.show()
|
||||||
|
|
||||||
mixpanel_track('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
trackEvent('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -115,7 +114,7 @@ async function install(instance) {
|
|||||||
instance.installedMod = true
|
instance.installedMod = true
|
||||||
instance.installing = false
|
instance.installing = false
|
||||||
|
|
||||||
mixpanel_track('ProjectInstall', {
|
trackEvent('ProjectInstall', {
|
||||||
loader: instance.loader,
|
loader: instance.loader,
|
||||||
game_version: instance.game_version,
|
game_version: instance.game_version,
|
||||||
id: project.value.id,
|
id: project.value.id,
|
||||||
@@ -137,12 +136,12 @@ const toggleCreation = () => {
|
|||||||
loader.value = null
|
loader.value = null
|
||||||
|
|
||||||
if (showCreation.value) {
|
if (showCreation.value) {
|
||||||
mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
trackEvent('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const upload_icon = async () => {
|
const upload_icon = async () => {
|
||||||
icon.value = await open({
|
const res = await open({
|
||||||
multiple: false,
|
multiple: false,
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
@@ -151,9 +150,10 @@ const upload_icon = async () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
icon.value = res.path ?? res
|
||||||
|
|
||||||
if (!icon.value) return
|
if (!icon.value) return
|
||||||
display_icon.value = tauri.convertFileSrc(icon.value)
|
display_icon.value = convertFileSrc(icon.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const reset_icon = () => {
|
const reset_icon = () => {
|
||||||
@@ -186,7 +186,7 @@ const createInstance = async () => {
|
|||||||
const instance = await get(id, true)
|
const instance = await get(id, true)
|
||||||
await installVersionDependencies(instance, versions.value[0])
|
await installVersionDependencies(instance, versions.value[0])
|
||||||
|
|
||||||
mixpanel_track('InstanceCreate', {
|
trackEvent('InstanceCreate', {
|
||||||
profile_name: name.value,
|
profile_name: name.value,
|
||||||
game_version: versions.value[0].game_versions[0],
|
game_version: versions.value[0].game_versions[0],
|
||||||
loader: loader,
|
loader: loader,
|
||||||
@@ -195,7 +195,7 @@ const createInstance = async () => {
|
|||||||
source: 'ProjectInstallModal',
|
source: 'ProjectInstallModal',
|
||||||
})
|
})
|
||||||
|
|
||||||
mixpanel_track('ProjectInstall', {
|
trackEvent('ProjectInstall', {
|
||||||
loader: loader,
|
loader: loader,
|
||||||
game_version: versions.value[0].game_versions[0],
|
game_version: versions.value[0].game_versions[0],
|
||||||
id: project.value,
|
id: project.value,
|
||||||
@@ -213,12 +213,7 @@ const createInstance = async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<ModalWrapper ref="installModal" header="Install project to instance" :on-hide="onInstall">
|
||||||
ref="installModal"
|
|
||||||
header="Install project to instance"
|
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
:on-hide="onInstall"
|
|
||||||
>
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input
|
<input
|
||||||
v-model="searchFilter"
|
v-model="searchFilter"
|
||||||
@@ -235,7 +230,7 @@ const createInstance = async () => {
|
|||||||
@click="installModal.hide()"
|
@click="installModal.hide()"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
:src="profile.icon_path ? tauri.convertFileSrc(profile.icon_path) : null"
|
:src="profile.icon_path ? convertFileSrc(profile.icon_path) : null"
|
||||||
class="profile-image"
|
class="profile-image"
|
||||||
/>
|
/>
|
||||||
{{ profile.name }}
|
{{ profile.name }}
|
||||||
@@ -304,7 +299,7 @@ const createInstance = async () => {
|
|||||||
<Button @click="installModal.hide()">Cancel</Button>
|
<Button @click="installModal.hide()">Cancel</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ConfirmModal } from '@modrinth/ui'
|
||||||
|
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
||||||
|
import { useTheming } from '@/store/theme.js'
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
confirmationText: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
hasToType: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: 'No title defined',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: 'No description defined',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
proceedLabel: {
|
||||||
|
type: String,
|
||||||
|
default: 'Proceed',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['proceed'])
|
||||||
|
const modal = ref(null)
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show: () => {
|
||||||
|
hide_ads_window()
|
||||||
|
modal.value.show()
|
||||||
|
},
|
||||||
|
hide: () => {
|
||||||
|
onModalHide()
|
||||||
|
modal.value.hide()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function onModalHide() {
|
||||||
|
show_ads_window()
|
||||||
|
}
|
||||||
|
|
||||||
|
function proceed() {
|
||||||
|
emit('proceed')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
ref="modal"
|
||||||
|
:confirmation-text="confirmationText"
|
||||||
|
:has-to-type="hasToType"
|
||||||
|
:title="title"
|
||||||
|
:description="description"
|
||||||
|
:proceed-label="proceedLabel"
|
||||||
|
:on-hide="onModalHide"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
@proceed="proceed"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
49
apps/app-frontend/src/components/ui/modal/ModalWrapper.vue
Normal file
49
apps/app-frontend/src/components/ui/modal/ModalWrapper.vue
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Modal } from '@modrinth/ui'
|
||||||
|
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
||||||
|
import { useTheming } from '@/store/theme.js'
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
header: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
closable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
onHide: {
|
||||||
|
type: Function,
|
||||||
|
default() {
|
||||||
|
return () => {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const modal = ref(null)
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show: () => {
|
||||||
|
hide_ads_window()
|
||||||
|
modal.value.show()
|
||||||
|
},
|
||||||
|
hide: () => {
|
||||||
|
onModalHide()
|
||||||
|
modal.value.hide()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function onModalHide() {
|
||||||
|
show_ads_window()
|
||||||
|
props.onHide()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal ref="modal" :header="header" :noblur="!themeStore.advancedRendering" @hide="onModalHide">
|
||||||
|
<slot />
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ShareModal } from '@modrinth/ui'
|
||||||
|
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
||||||
|
import { useTheming } from '@/store/theme.js'
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
header: {
|
||||||
|
type: String,
|
||||||
|
default: 'Share',
|
||||||
|
},
|
||||||
|
shareTitle: {
|
||||||
|
type: String,
|
||||||
|
default: 'Modrinth',
|
||||||
|
},
|
||||||
|
shareText: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
openInNewTab: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const modal = ref(null)
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show: (passedContent) => {
|
||||||
|
hide_ads_window()
|
||||||
|
modal.value.show(passedContent)
|
||||||
|
},
|
||||||
|
hide: () => {
|
||||||
|
onModalHide()
|
||||||
|
modal.value.hide()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function onModalHide() {
|
||||||
|
show_ads_window()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ShareModal
|
||||||
|
ref="modal"
|
||||||
|
:header="header"
|
||||||
|
:share-title="shareTitle"
|
||||||
|
:share-text="shareText"
|
||||||
|
:link="link"
|
||||||
|
:open-in-new-tab="openInNewTab"
|
||||||
|
:on-hide="onModalHide"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { UserIcon, LockIcon, MailIcon } from '@modrinth/assets'
|
import { UserIcon, LockIcon, MailIcon } from '@modrinth/assets'
|
||||||
import { Button, Card, Checkbox, Modal } from '@modrinth/ui'
|
import { Button, Card, Checkbox } from '@modrinth/ui'
|
||||||
import {
|
import {
|
||||||
DiscordIcon,
|
DiscordIcon,
|
||||||
GithubIcon,
|
GithubIcon,
|
||||||
@@ -13,6 +13,7 @@ import { login, login_2fa, create_account, login_pass } from '@/helpers/mr_auth.
|
|||||||
import { handleError, useNotifications } from '@/store/state.js'
|
import { handleError, useNotifications } from '@/store/state.js'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
callback: {
|
callback: {
|
||||||
@@ -132,7 +133,7 @@ async function createAccount() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal ref="modal" :on-hide="removeWidget">
|
<ModalWrapper ref="modal" :on-hide="removeWidget">
|
||||||
<Card>
|
<Card>
|
||||||
<template v-if="twoFactorFlow">
|
<template v-if="twoFactorFlow">
|
||||||
<h1>Enter two-factor code</h1>
|
<h1>Enter two-factor code</h1>
|
||||||
@@ -217,17 +218,17 @@ async function createAccount() {
|
|||||||
v-else-if="loggingIn"
|
v-else-if="loggingIn"
|
||||||
color="primary"
|
color="primary"
|
||||||
large
|
large
|
||||||
@click="signIn"
|
|
||||||
:disabled="!turnstileToken"
|
:disabled="!turnstileToken"
|
||||||
|
@click="signIn"
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</Button>
|
</Button>
|
||||||
<Button v-else color="primary" large @click="createAccount" :disabled="!turnstileToken">
|
<Button v-else color="primary" large :disabled="!turnstileToken" @click="createAccount">
|
||||||
Create account
|
Create account
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
20
apps/app-frontend/src/composables/macCssFix.js
Normal file
20
apps/app-frontend/src/composables/macCssFix.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
import cssContent from '@/assets/stylesheets/macFix.css?inline'
|
||||||
|
|
||||||
|
export async function useCheckDisableMouseover() {
|
||||||
|
try {
|
||||||
|
// Fetch the CSS content from the Rust backend
|
||||||
|
let should_disable_mouseover = await invoke('plugin:utils|should_disable_mouseover')
|
||||||
|
|
||||||
|
if (should_disable_mouseover) {
|
||||||
|
// Create a style element and set its content
|
||||||
|
const styleElement = document.createElement('style')
|
||||||
|
styleElement.innerHTML = cssContent
|
||||||
|
|
||||||
|
// Append the style element to the document's head
|
||||||
|
document.head.appendChild(styleElement)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking OS version from Rust backend', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
21
apps/app-frontend/src/helpers/ads.js
Normal file
21
apps/app-frontend/src/helpers/ads.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
|
export async function init_ads_window(x, y, width, height, overrideShown = false) {
|
||||||
|
return await invoke('plugin:ads|init_ads_window', { x, y, width, height, overrideShown })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function show_ads_window() {
|
||||||
|
return await invoke('plugin:ads|show_ads_window')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function hide_ads_window(reset) {
|
||||||
|
return await invoke('plugin:ads|hide_ads_window', { reset })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function record_ads_click() {
|
||||||
|
return await invoke('plugin:ads|record_ads_click')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function open_ads_link(path, origin) {
|
||||||
|
return await invoke('plugin:ads|open_link', { path, origin })
|
||||||
|
}
|
||||||
23
apps/app-frontend/src/helpers/analytics.js
Normal file
23
apps/app-frontend/src/helpers/analytics.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { posthog } from 'posthog-js'
|
||||||
|
|
||||||
|
export const initAnalytics = () => {
|
||||||
|
posthog.init('phc_hm2ihMpTAoE86xIm7XzsCB8RPiTRKivViK5biiHedm', {
|
||||||
|
persistence: 'localStorage',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const debugAnalytics = () => {
|
||||||
|
posthog.debug()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const optOutAnalytics = () => {
|
||||||
|
posthog.opt_out_capturing()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const optInAnalytics = () => {
|
||||||
|
posthog.opt_in_capturing()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const trackEvent = (eventName, properties) => {
|
||||||
|
posthog.capture(eventName, properties)
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
// Example function:
|
// Example function:
|
||||||
// User goes to auth_url to complete flow, and when completed, authenticate_await_completion() returns the credentials
|
// User goes to auth_url to complete flow, and when completed, authenticate_await_completion() returns the credentials
|
||||||
@@ -13,35 +13,46 @@ import { invoke } from '@tauri-apps/api/tauri'
|
|||||||
// await authenticate_await_completion()
|
// await authenticate_await_completion()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/// Authenticate a user with Hydra - part 1
|
/**
|
||||||
/// This begins the authentication flow quasi-synchronously
|
* Authenticate a user with Hydra - part 1.
|
||||||
/// This returns a DeviceLoginSuccess object, with two relevant fields:
|
* This begins the authentication flow quasi-synchronously.
|
||||||
/// - verification_uri: the URL to go to to complete the flow
|
*
|
||||||
/// - user_code: the code to enter on the verification_uri page
|
* @returns {Promise<DeviceLoginSuccess>} A DeviceLoginSuccess object with two relevant fields:
|
||||||
|
* @property {string} verification_uri - The URL to go to complete the flow.
|
||||||
|
* @property {string} user_code - The code to enter on the verification_uri page.
|
||||||
|
*/
|
||||||
export async function login() {
|
export async function login() {
|
||||||
return await invoke('auth_login')
|
return await invoke('plugin:auth|login')
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the default user
|
/**
|
||||||
/// user is UUID
|
* Retrieves the default user
|
||||||
|
* @return {Promise<UUID | undefined>}
|
||||||
|
*/
|
||||||
export async function get_default_user() {
|
export async function get_default_user() {
|
||||||
return await invoke('plugin:auth|auth_get_default_user')
|
return await invoke('plugin:auth|get_default_user')
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the default user
|
/**
|
||||||
/// user is UUID
|
* Updates the default user
|
||||||
|
* @param {UUID} user
|
||||||
|
*/
|
||||||
export async function set_default_user(user) {
|
export async function set_default_user(user) {
|
||||||
return await invoke('plugin:auth|auth_set_default_user', { user })
|
return await invoke('plugin:auth|set_default_user', { user })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a user account from the database
|
/**
|
||||||
/// user is UUID
|
* Remove a user account from the database
|
||||||
|
* @param {UUID} user
|
||||||
|
*/
|
||||||
export async function remove_user(user) {
|
export async function remove_user(user) {
|
||||||
return await invoke('plugin:auth|auth_remove_user', { user })
|
return await invoke('plugin:auth|remove_user', { user })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of users
|
/**
|
||||||
/// Returns an Array of Credentials
|
* Returns a list of users
|
||||||
|
* @returns {Promise<Credential[]>}
|
||||||
|
*/
|
||||||
export async function users() {
|
export async function users() {
|
||||||
return await invoke('plugin:auth|auth_users')
|
return await invoke('plugin:auth|get_users')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
export async function get_project(id, cacheBehaviour) {
|
export async function get_project(id, cacheBehaviour) {
|
||||||
return await invoke('plugin:cache|get_project', { id, cacheBehaviour })
|
return await invoke('plugin:cache|get_project', { id, cacheBehaviour })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { create } from './profile'
|
import { create } from './profile'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -27,7 +27,7 @@ import { create } from './profile'
|
|||||||
/// eg: get_importable_instances("MultiMC", "C:/MultiMC")
|
/// eg: get_importable_instances("MultiMC", "C:/MultiMC")
|
||||||
/// returns ["Instance 1", "Instance 2"]
|
/// returns ["Instance 1", "Instance 2"]
|
||||||
export async function get_importable_instances(launcherType, basePath) {
|
export async function get_importable_instances(launcherType, basePath) {
|
||||||
return await invoke('plugin:import|import_get_importable_instances', { launcherType, basePath })
|
return await invoke('plugin:import|get_importable_instances', { launcherType, basePath })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import an instance from a launcher type and base path
|
/// Import an instance from a launcher type and base path
|
||||||
@@ -38,7 +38,7 @@ export async function import_instance(launcherType, basePath, instanceFolder) {
|
|||||||
// fs watching will be enabled once the instance is imported
|
// fs watching will be enabled once the instance is imported
|
||||||
const profilePath = await create(instanceFolder, '1.19.4', 'vanilla', 'latest', null, true)
|
const profilePath = await create(instanceFolder, '1.19.4', 'vanilla', 'latest', null, true)
|
||||||
|
|
||||||
return await invoke('plugin:import|import_import_instance', {
|
return await invoke('plugin:import|import_instance', {
|
||||||
profilePath,
|
profilePath,
|
||||||
launcherType,
|
launcherType,
|
||||||
basePath,
|
basePath,
|
||||||
@@ -49,7 +49,7 @@ export async function import_instance(launcherType, basePath, instanceFolder) {
|
|||||||
/// Checks if this instance is valid for importing, given a certain launcher type
|
/// Checks if this instance is valid for importing, given a certain launcher type
|
||||||
/// eg: is_valid_importable_instance("C:/MultiMC/Instance 1", "MultiMC")
|
/// eg: is_valid_importable_instance("C:/MultiMC/Instance 1", "MultiMC")
|
||||||
export async function is_valid_importable_instance(instanceFolder, launcherType) {
|
export async function is_valid_importable_instance(instanceFolder, launcherType) {
|
||||||
return await invoke('plugin:import|import_is_valid_importable_instance', {
|
return await invoke('plugin:import|is_valid_importable_instance', {
|
||||||
instanceFolder,
|
instanceFolder,
|
||||||
launcherType,
|
launcherType,
|
||||||
})
|
})
|
||||||
@@ -59,5 +59,5 @@ export async function is_valid_importable_instance(instanceFolder, launcherType)
|
|||||||
/// null if it can't be found or doesn't exist
|
/// null if it can't be found or doesn't exist
|
||||||
/// eg: get_default_launcher_path("MultiMC")
|
/// eg: get_default_launcher_path("MultiMC")
|
||||||
export async function get_default_launcher_path(launcherType) {
|
export async function get_default_launcher_path(launcherType) {
|
||||||
return await invoke('plugin:import|import_get_default_launcher_path', { launcherType })
|
return await invoke('plugin:import|get_default_launcher_path', { launcherType })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A log is a struct containing the filename string, stdout, and stderr, as follows:
|
A log is a struct containing the filename string, stdout, and stderr, as follows:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
/// Gets the game versions from daedalus
|
/// Gets the game versions from daedalus
|
||||||
// Returns a VersionManifest
|
// Returns a VersionManifest
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
import mixpanel from 'mixpanel-browser'
|
|
||||||
|
|
||||||
// mixpanel_track
|
|
||||||
function trackWrapper(originalTrack) {
|
|
||||||
return function (event_name, properties = {}) {
|
|
||||||
try {
|
|
||||||
originalTrack(event_name, properties)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export const mixpanel_track = trackWrapper(mixpanel.track.bind(mixpanel))
|
|
||||||
|
|
||||||
// mixpanel_opt_out_tracking()
|
|
||||||
function optOutTrackingWrapper(originalOptOutTracking) {
|
|
||||||
return function () {
|
|
||||||
try {
|
|
||||||
originalOptOutTracking()
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export const mixpanel_opt_out_tracking = optOutTrackingWrapper(
|
|
||||||
mixpanel.opt_out_tracking.bind(mixpanel),
|
|
||||||
)
|
|
||||||
|
|
||||||
// mixpanel_opt_in_tracking()
|
|
||||||
function optInTrackingWrapper(originalOptInTracking) {
|
|
||||||
return function () {
|
|
||||||
try {
|
|
||||||
originalOptInTracking()
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export const mixpanel_opt_in_tracking = optInTrackingWrapper(
|
|
||||||
mixpanel.opt_in_tracking.bind(mixpanel),
|
|
||||||
)
|
|
||||||
|
|
||||||
// mixpanel_init
|
|
||||||
function initWrapper(originalInit) {
|
|
||||||
return function (token, config = {}) {
|
|
||||||
try {
|
|
||||||
originalInit(token, config)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export const mixpanel_init = initWrapper(mixpanel.init.bind(mixpanel))
|
|
||||||
|
|
||||||
export const mixpanel_is_loaded = () => {
|
|
||||||
return mixpanel.__loaded
|
|
||||||
}
|
|
||||||
@@ -3,22 +3,22 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
export async function login(provider) {
|
export async function login(provider) {
|
||||||
return await invoke('modrinth_auth_login', { provider })
|
return await invoke('modrinth_auth_login', { provider })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login_pass(username, password, challenge) {
|
export async function login_pass(username, password, challenge) {
|
||||||
return await invoke('plugin:mr_auth|login_pass', { username, password, challenge })
|
return await invoke('plugin:mr-auth|login_pass', { username, password, challenge })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login_2fa(code, flow) {
|
export async function login_2fa(code, flow) {
|
||||||
return await invoke('plugin:mr_auth|login_2fa', { code, flow })
|
return await invoke('plugin:mr-auth|login_2fa', { code, flow })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create_account(username, email, password, challenge, signUpNewsletter) {
|
export async function create_account(username, email, password, challenge, signUpNewsletter) {
|
||||||
return await invoke('plugin:mr_auth|create_account', {
|
return await invoke('plugin:mr-auth|create_account', {
|
||||||
username,
|
username,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
@@ -28,9 +28,9 @@ export async function create_account(username, email, password, challenge, signU
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function logout() {
|
export async function logout() {
|
||||||
return await invoke('plugin:mr_auth|logout')
|
return await invoke('plugin:mr-auth|logout')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get() {
|
export async function get() {
|
||||||
return await invoke('plugin:mr_auth|get')
|
return await invoke('plugin:mr-auth|get')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { create } from './profile'
|
import { create } from './profile'
|
||||||
|
|
||||||
// Installs pack from a version ID
|
// Installs pack from a version ID
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
/// Gets all running process IDs with a given profile path
|
/// Gets all running process IDs with a given profile path
|
||||||
/// Returns [u32]
|
/// Returns [u32]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
/// Add instance
|
/// Add instance
|
||||||
/*
|
/*
|
||||||
@@ -19,7 +19,7 @@ import { invoke } from '@tauri-apps/api/tauri'
|
|||||||
export async function create(name, gameVersion, modloader, loaderVersion, iconPath, skipInstall) {
|
export async function create(name, gameVersion, modloader, loaderVersion, iconPath, skipInstall) {
|
||||||
//Trim string name to avoid "Unable to find directory"
|
//Trim string name to avoid "Unable to find directory"
|
||||||
name = name.trim()
|
name = name.trim()
|
||||||
return await invoke('plugin:profile_create|profile_create', {
|
return await invoke('plugin:profile-create|profile_create', {
|
||||||
name,
|
name,
|
||||||
gameVersion,
|
gameVersion,
|
||||||
modloader,
|
modloader,
|
||||||
@@ -31,7 +31,7 @@ export async function create(name, gameVersion, modloader, loaderVersion, iconPa
|
|||||||
|
|
||||||
// duplicate a profile
|
// duplicate a profile
|
||||||
export async function duplicate(path) {
|
export async function duplicate(path) {
|
||||||
return await invoke('plugin:profile_create|profile_duplicate', { path })
|
return await invoke('plugin:profile-create|profile_duplicate', { path })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a profile
|
// Remove a profile
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
// Settings object
|
// Settings object
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
// Initialize the theseus API state
|
// Initialize the theseus API state
|
||||||
// This should be called during the initializion/opening of the launcher
|
// This should be called during the initializion/opening of the launcher
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
// Gets cached category tags
|
// Gets cached category tags
|
||||||
export async function get_categories() {
|
export async function get_categories() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { get_full_path, get_mod_full_path } from '@/helpers/profile'
|
import { get_full_path, get_mod_full_path } from '@/helpers/profile'
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
export async function isDev() {
|
export async function isDev() {
|
||||||
return await invoke('is_dev')
|
return await invoke('is_dev')
|
||||||
@@ -33,6 +33,10 @@ export async function highlightModInProfile(profilePath, projectPath) {
|
|||||||
return await highlightInFolder(fullPath)
|
return await highlightInFolder(fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function restartApp() {
|
||||||
|
return await invoke('restart_app')
|
||||||
|
}
|
||||||
|
|
||||||
export const releaseColor = (releaseType) => {
|
export const releaseColor = (releaseType) => {
|
||||||
switch (releaseType) {
|
switch (releaseType) {
|
||||||
case 'release':
|
case 'release':
|
||||||
@@ -52,7 +56,7 @@ export function debounce(fn, wait) {
|
|||||||
if (timer) {
|
if (timer) {
|
||||||
clearTimeout(timer) // clear any pre-existing timer
|
clearTimeout(timer) // clear any pre-existing timer
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
||||||
const context = this // get the current context
|
const context = this // get the current context
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
fn.apply(context, args) // call the function if time expires
|
fn.apply(context, args) // call the function if time expires
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import App from '@/App.vue'
|
|||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import FloatingVue from 'floating-vue'
|
import FloatingVue from 'floating-vue'
|
||||||
import 'floating-vue/dist/style.css'
|
import 'floating-vue/dist/style.css'
|
||||||
import loadCssMixin from './mixins/macCssFix.js'
|
|
||||||
import { createPlugin } from '@vintl/vintl/plugin'
|
import { createPlugin } from '@vintl/vintl/plugin'
|
||||||
|
import * as Sentry from '@sentry/vue'
|
||||||
|
|
||||||
const VIntlPlugin = createPlugin({
|
const VIntlPlugin = createPlugin({
|
||||||
controllerOpts: {
|
controllerOpts: {
|
||||||
@@ -27,10 +27,17 @@ const VIntlPlugin = createPlugin({
|
|||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|
||||||
let app = createApp(App)
|
let app = createApp(App)
|
||||||
|
|
||||||
|
Sentry.init({
|
||||||
|
app,
|
||||||
|
dsn: 'https://9508775ee5034536bc70433f5f531dd4@o485889.ingest.us.sentry.io/4504579615227904',
|
||||||
|
integrations: [Sentry.browserTracingIntegration({ router })],
|
||||||
|
tracesSampleRate: 0.1,
|
||||||
|
})
|
||||||
|
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.use(FloatingVue)
|
app.use(FloatingVue)
|
||||||
app.mixin(loadCssMixin)
|
|
||||||
app.use(VIntlPlugin)
|
app.use(VIntlPlugin)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import { invoke } from '@tauri-apps/api/tauri'
|
|
||||||
import cssContent from '@/assets/stylesheets/macFix.css?inline'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
async mounted() {
|
|
||||||
await this.checkDisableMouseover()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async checkDisableMouseover() {
|
|
||||||
try {
|
|
||||||
// Fetch the CSS content from the Rust backend
|
|
||||||
const should_disable_mouseover = await invoke('plugin:utils|should_disable_mouseover')
|
|
||||||
|
|
||||||
if (should_disable_mouseover) {
|
|
||||||
// Create a style element and set its content
|
|
||||||
const styleElement = document.createElement('style')
|
|
||||||
styleElement.innerHTML = cssContent
|
|
||||||
|
|
||||||
// Append the style element to the document's head
|
|
||||||
document.head.appendChild(styleElement)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error checking OS version from Rust backend', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,7 @@ import { get_categories, get_loaders, get_game_versions } from '@/helpers/tags'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import SearchCard from '@/components/ui/SearchCard.vue'
|
import SearchCard from '@/components/ui/SearchCard.vue'
|
||||||
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
|
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||||
import { get_search_results } from '@/helpers/cache.js'
|
import { get_search_results } from '@/helpers/cache.js'
|
||||||
import { debounce } from '@/helpers/utils.js'
|
import { debounce } from '@/helpers/utils.js'
|
||||||
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
||||||
@@ -381,20 +381,20 @@ const sortedCategories = computed(() => {
|
|||||||
// identifier[0], then if it ties, identifier[1], etc
|
// identifier[0], then if it ties, identifier[1], etc
|
||||||
async function sortByNameOrNumber(sortable, identifiers) {
|
async function sortByNameOrNumber(sortable, identifiers) {
|
||||||
sortable.sort((a, b) => {
|
sortable.sort((a, b) => {
|
||||||
for (let identifier of identifiers) {
|
for (const identifier of identifiers) {
|
||||||
let aNum = parseFloat(a[identifier])
|
const aNum = parseFloat(a[identifier])
|
||||||
let bNum = parseFloat(b[identifier])
|
const bNum = parseFloat(b[identifier])
|
||||||
if (isNaN(aNum) && isNaN(bNum)) {
|
if (isNaN(aNum) && isNaN(bNum)) {
|
||||||
// Both are strings, sort alphabetically
|
// Both are strings, sort alphabetically
|
||||||
let stringComp = a[identifier].localeCompare(b[identifier])
|
const stringComp = a[identifier].localeCompare(b[identifier])
|
||||||
if (stringComp != 0) return stringComp
|
if (stringComp != 0) return stringComp
|
||||||
} else if (!isNaN(aNum) && !isNaN(bNum)) {
|
} else if (!isNaN(aNum) && !isNaN(bNum)) {
|
||||||
// Both are numbers, sort numerically
|
// Both are numbers, sort numerically
|
||||||
let numComp = aNum - bNum
|
const numComp = aNum - bNum
|
||||||
if (numComp != 0) return numComp
|
if (numComp != 0) return numComp
|
||||||
} else {
|
} else {
|
||||||
// One is a number and one is a string, numbers go first
|
// One is a number and one is a string, numbers go first
|
||||||
let numStringComp = isNaN(aNum) ? 1 : -1
|
const numStringComp = isNaN(aNum) ? 1 : -1
|
||||||
if (numStringComp != 0) return numStringComp
|
if (numStringComp != 0) return numStringComp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -528,7 +528,8 @@ const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.valu
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="searchWrapper" class="search-container">
|
<div ref="searchWrapper" class="search-container">
|
||||||
<aside class="filter-panel">
|
<aside class="filter-panel" @scroll="$refs.promo.scroll()">
|
||||||
|
<PromotionWrapper ref="promo" />
|
||||||
<Card v-if="instanceContext" class="small-instance">
|
<Card v-if="instanceContext" class="small-instance">
|
||||||
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
||||||
<Avatar
|
<Avatar
|
||||||
@@ -675,8 +676,7 @@ const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.valu
|
|||||||
</Card>
|
</Card>
|
||||||
</aside>
|
</aside>
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<PromotionWrapper class="mt-4" />
|
<Card class="project-type-container mt-4">
|
||||||
<Card class="project-type-container">
|
|
||||||
<NavRow :links="selectableProjectTypes" />
|
<NavRow :links="selectableProjectTypes" />
|
||||||
</Card>
|
</Card>
|
||||||
<Card class="search-panel-container">
|
<Card class="search-panel-container">
|
||||||
@@ -878,13 +878,13 @@ const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.valu
|
|||||||
|
|
||||||
.filter-panel {
|
.filter-panel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 20rem;
|
|
||||||
padding: 1rem 0.5rem 1rem 1rem;
|
padding: 1rem 0.5rem 1rem 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
min-height: calc(100vh - 3.25rem);
|
min-height: calc(100vh - 3.25rem);
|
||||||
max-height: calc(100vh - 3.25rem);
|
max-height: calc(100vh - 3.25rem);
|
||||||
|
width: 20rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
@@ -903,8 +903,8 @@ const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.valu
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
margin: 0 1rem 0.5rem 20.5rem;
|
margin: 0 1rem 0.5rem calc(20rem + 1rem);
|
||||||
width: calc(100% - 20.5rem);
|
width: calc(100% - calc(20rem + 1rem));
|
||||||
|
|
||||||
.offline {
|
.offline {
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onUnmounted, computed } from 'vue'
|
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import RowDisplay from '@/components/RowDisplay.vue'
|
import RowDisplay from '@/components/RowDisplay.vue'
|
||||||
import { list } from '@/helpers/profile.js'
|
import { list } from '@/helpers/profile.js'
|
||||||
@@ -8,6 +8,11 @@ import { useBreadcrumbs } from '@/store/breadcrumbs'
|
|||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { get_search_results } from '@/helpers/cache.js'
|
import { get_search_results } from '@/helpers/cache.js'
|
||||||
|
import { hide_ads_window } from '@/helpers/ads.js'
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
hide_ads_window(true)
|
||||||
|
})
|
||||||
|
|
||||||
const featuredModpacks = ref({})
|
const featuredModpacks = ref({})
|
||||||
const featuredMods = ref({})
|
const featuredMods = ref({})
|
||||||
@@ -42,7 +47,7 @@ const getInstances = async () => {
|
|||||||
return dateB - dateA
|
return dateB - dateA
|
||||||
})
|
})
|
||||||
|
|
||||||
let filters = []
|
const filters = []
|
||||||
for (const instance of recentInstances.value) {
|
for (const instance of recentInstances.value) {
|
||||||
if (instance.linked_data && instance.linked_data.project_id) {
|
if (instance.linked_data && instance.linked_data.project_id) {
|
||||||
filters.push(`NOT"project_id"="${instance.linked_data.project_id}"`)
|
filters.push(`NOT"project_id"="${instance.linked_data.project_id}"`)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onUnmounted, ref, shallowRef } from 'vue'
|
import { onMounted, onUnmounted, ref, shallowRef } from 'vue'
|
||||||
import GridDisplay from '@/components/GridDisplay.vue'
|
import GridDisplay from '@/components/GridDisplay.vue'
|
||||||
import { list } from '@/helpers/profile.js'
|
import { list } from '@/helpers/profile.js'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
@@ -10,6 +10,11 @@ import { Button } from '@modrinth/ui'
|
|||||||
import { PlusIcon } from '@modrinth/assets'
|
import { PlusIcon } from '@modrinth/assets'
|
||||||
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
||||||
import { NewInstanceImage } from '@/assets/icons'
|
import { NewInstanceImage } from '@/assets/icons'
|
||||||
|
import { hide_ads_window } from '@/helpers/ads.js'
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
hide_ads_window(true)
|
||||||
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch, onMounted } from 'vue'
|
||||||
import { LogOutIcon, LogInIcon, BoxIcon, FolderSearchIcon, TrashIcon } from '@modrinth/assets'
|
import { LogOutIcon, LogInIcon, BoxIcon, FolderSearchIcon, TrashIcon } from '@modrinth/assets'
|
||||||
import { Card, Slider, DropdownSelect, Toggle, ConfirmModal, Button } from '@modrinth/ui'
|
import { Card, Slider, DropdownSelect, Toggle, Button } from '@modrinth/ui'
|
||||||
import { handleError, useTheming } from '@/store/state'
|
import { handleError, useTheming } from '@/store/state'
|
||||||
import { get, set } from '@/helpers/settings'
|
import { get, set } from '@/helpers/settings'
|
||||||
import { get_java_versions, get_max_memory, set_java_version } from '@/helpers/jre'
|
import { get_java_versions, get_max_memory, set_java_version } from '@/helpers/jre'
|
||||||
import { get as getCreds, logout } from '@/helpers/mr_auth.js'
|
import { get as getCreds, logout } from '@/helpers/mr_auth.js'
|
||||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||||
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
|
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
|
||||||
import { mixpanel_opt_out_tracking, mixpanel_opt_in_tracking } from '@/helpers/mixpanel'
|
import { optOutAnalytics, optInAnalytics } from '@/helpers/analytics'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/plugin-dialog'
|
||||||
import { getOS } from '@/helpers/utils.js'
|
import { getOS } from '@/helpers/utils.js'
|
||||||
import { getVersion } from '@tauri-apps/api/app'
|
import { getVersion } from '@tauri-apps/api/app'
|
||||||
import { get_user, purge_cache_types } from '@/helpers/cache.js'
|
import { get_user, purge_cache_types } from '@/helpers/cache.js'
|
||||||
|
import { hide_ads_window } from '@/helpers/ads.js'
|
||||||
|
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
hide_ads_window(true)
|
||||||
|
})
|
||||||
|
|
||||||
const pageOptions = ['Home', 'Library']
|
const pageOptions = ['Home', 'Library']
|
||||||
|
|
||||||
@@ -45,9 +51,9 @@ watch(
|
|||||||
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
||||||
|
|
||||||
if (setSettings.telemetry) {
|
if (setSettings.telemetry) {
|
||||||
mixpanel_opt_out_tracking()
|
optInAnalytics()
|
||||||
} else {
|
} else {
|
||||||
mixpanel_opt_in_tracking()
|
optOutAnalytics()
|
||||||
}
|
}
|
||||||
|
|
||||||
setSettings.extra_launch_args = setSettings.launchArgs.trim().split(/\s+/).filter(Boolean)
|
setSettings.extra_launch_args = setSettings.launchArgs.trim().split(/\s+/).filter(Boolean)
|
||||||
@@ -169,13 +175,12 @@ async function purgeCache() {
|
|||||||
Sign in
|
Sign in
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmModal
|
<ConfirmModalWrapper
|
||||||
ref="purgeCacheConfirmModal"
|
ref="purgeCacheConfirmModal"
|
||||||
title="Are you sure you want to purge the cache?"
|
title="Are you sure you want to purge the cache?"
|
||||||
description="If you proceed, your entire cache will be purged. This may slow down the app temporarily."
|
description="If you proceed, your entire cache will be purged. This may slow down the app temporarily."
|
||||||
:has-to-type="false"
|
:has-to-type="false"
|
||||||
proceed-label="Purge cache"
|
proceed-label="Purge cache"
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
@proceed="purgeCache"
|
@proceed="purgeCache"
|
||||||
/>
|
/>
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
@@ -358,6 +363,25 @@ async function purgeCache() {
|
|||||||
<span class="label__title size-card-header">Privacy</span>
|
<span class="label__title size-card-header">Privacy</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="adjacent-input">
|
||||||
|
<label for="opt-out-analytics">
|
||||||
|
<span class="label__title">Personalized ads</span>
|
||||||
|
<span class="label__description">
|
||||||
|
Modrinth's ad provider, Aditude, shows ads based on your preferences. By disabling this
|
||||||
|
option, you opt out and ads will no longer be shown based on your interests.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<Toggle
|
||||||
|
id="opt-out-analytics"
|
||||||
|
:model-value="settings.personalized_ads"
|
||||||
|
:checked="settings.personalized_ads"
|
||||||
|
@update:model-value="
|
||||||
|
(e) => {
|
||||||
|
settings.personalized_ads = e
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
<label for="opt-out-analytics">
|
<label for="opt-out-analytics">
|
||||||
<span class="label__title">Telemetry</span>
|
<span class="label__title">Telemetry</span>
|
||||||
@@ -401,14 +425,14 @@ async function purgeCache() {
|
|||||||
<span class="label__title size-card-header">Java settings</span>
|
<span class="label__title size-card-header">Java settings</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<template v-for="version in [21, 17, 8]">
|
<template v-for="javaVersion in [21, 17, 8]" :key="`java-${javaVersion}`">
|
||||||
<label :for="'java-' + version">
|
<label :for="'java-' + javaVersion">
|
||||||
<span class="label__title">Java {{ version }} location</span>
|
<span class="label__title">Java {{ javaVersion }} location</span>
|
||||||
</label>
|
</label>
|
||||||
<JavaSelector
|
<JavaSelector
|
||||||
:id="'java-selector-' + version"
|
:id="'java-selector-' + javaVersion"
|
||||||
v-model="javaVersions[version]"
|
v-model="javaVersions[javaVersion]"
|
||||||
:version="version"
|
:version="javaVersion"
|
||||||
@update:model-value="updateJavaVersion"
|
@update:model-value="updateJavaVersion"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -449,7 +473,7 @@ async function purgeCache() {
|
|||||||
:min="8"
|
:min="8"
|
||||||
:max="maxMemory"
|
:max="maxMemory"
|
||||||
:step="64"
|
:step="64"
|
||||||
unit="mb"
|
unit="MB"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="instance-container">
|
<div class="instance-container">
|
||||||
<div class="side-cards">
|
<div class="side-cards pb-4" @scroll="$refs.promo.scroll()">
|
||||||
<Card class="instance-card" @contextmenu.prevent.stop="handleRightClick">
|
<Card class="instance-card" @contextmenu.prevent.stop="handleRightClick">
|
||||||
<Avatar size="lg" :src="instance.icon_path ? convertFileSrc(instance.icon_path) : null" />
|
<Avatar size="md" :src="instance.icon_path ? convertFileSrc(instance.icon_path) : null" />
|
||||||
<div class="instance-info">
|
<div class="instance-info">
|
||||||
<h2 class="name">{{ instance.name }}</h2>
|
<h2 class="name">{{ instance.name }}</h2>
|
||||||
<span class="metadata"> {{ instance.loader }} {{ instance.game_version }} </span>
|
<span class="metadata"> {{ instance.loader }} {{ instance.game_version }} </span>
|
||||||
@@ -61,9 +61,9 @@
|
|||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
<PromotionWrapper ref="promo" class="mt-4" />
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<PromotionWrapper />
|
|
||||||
<RouterView v-slot="{ Component }">
|
<RouterView v-slot="{ Component }">
|
||||||
<template v-if="Component">
|
<template v-if="Component">
|
||||||
<Suspense @pending="loadingBar.startLoading()" @resolve="loadingBar.stopLoading()">
|
<Suspense @pending="loadingBar.startLoading()" @resolve="loadingBar.stopLoading()">
|
||||||
@@ -131,9 +131,8 @@ import { ref, onUnmounted } from 'vue'
|
|||||||
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||||
import { useFetch } from '@/helpers/fetch'
|
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
import { get_project, get_version_many } from '@/helpers/cache.js'
|
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@@ -183,7 +182,7 @@ const startInstance = async (context) => {
|
|||||||
}
|
}
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|
||||||
mixpanel_track('InstanceStart', {
|
trackEvent('InstanceStart', {
|
||||||
loader: instance.value.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.game_version,
|
game_version: instance.value.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
@@ -220,7 +219,7 @@ const stopInstance = async (context) => {
|
|||||||
playing.value = false
|
playing.value = false
|
||||||
await kill(route.params.id).catch(handleError)
|
await kill(route.params.id).catch(handleError)
|
||||||
|
|
||||||
mixpanel_track('InstanceStop', {
|
trackEvent('InstanceStop', {
|
||||||
loader: instance.value.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.game_version,
|
game_version: instance.value.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
@@ -312,7 +311,6 @@ onUnmounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
width: 17rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
@@ -326,12 +324,13 @@ Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.side-cards {
|
.side-cards {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
|
width: 300px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 1rem;
|
|
||||||
min-height: calc(100% - 3.25rem);
|
min-height: calc(100vh - 3.25rem);
|
||||||
max-height: calc(100% - 3.25rem);
|
max-height: calc(100vh - 3.25rem);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
@@ -375,10 +374,7 @@ Button {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
padding: 1rem;
|
||||||
|
|
||||||
.content {
|
|
||||||
margin-left: 19rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.instance-info {
|
.instance-info {
|
||||||
@@ -452,10 +448,10 @@ Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
width: 100%;
|
margin: 0 1rem 0.5rem 20rem;
|
||||||
|
width: calc(100% - 20rem);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 1rem 1rem 0 0;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</RecycleScroller>
|
</RecycleScroller>
|
||||||
</div>
|
</div>
|
||||||
<ShareModal
|
<ShareModalWrapper
|
||||||
ref="shareModal"
|
ref="shareModal"
|
||||||
header="Share Log"
|
header="Share Log"
|
||||||
share-title="Instance Log"
|
share-title="Instance Log"
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { CheckIcon, ClipboardCopyIcon, ShareIcon, TrashIcon } from '@modrinth/assets'
|
import { CheckIcon, ClipboardCopyIcon, ShareIcon, TrashIcon } from '@modrinth/assets'
|
||||||
import { Button, Card, ShareModal, Checkbox, DropdownSelect } from '@modrinth/ui'
|
import { Button, Card, Checkbox, DropdownSelect } from '@modrinth/ui'
|
||||||
import {
|
import {
|
||||||
delete_logs_by_filename,
|
delete_logs_by_filename,
|
||||||
get_logs,
|
get_logs,
|
||||||
@@ -107,6 +107,7 @@ import { handleError } from '@/store/notifications.js'
|
|||||||
import { ofetch } from 'ofetch'
|
import { ofetch } from 'ofetch'
|
||||||
import { RecycleScroller } from 'vue-virtual-scroller'
|
import { RecycleScroller } from 'vue-virtual-scroller'
|
||||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||||
|
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
|
||||||
|
|
||||||
dayjs.extend(isToday)
|
dayjs.extend(isToday)
|
||||||
dayjs.extend(isYesterday)
|
dayjs.extend(isYesterday)
|
||||||
@@ -295,7 +296,7 @@ if (logs.value.length > 1 && !props.playing) {
|
|||||||
|
|
||||||
const deleteLog = async () => {
|
const deleteLog = async () => {
|
||||||
if (logs.value[selectedLogIndex.value] && selectedLogIndex.value !== 0) {
|
if (logs.value[selectedLogIndex.value] && selectedLogIndex.value !== 0) {
|
||||||
let deleteIndex = selectedLogIndex.value
|
const deleteIndex = selectedLogIndex.value
|
||||||
selectedLogIndex.value = deleteIndex - 1
|
selectedLogIndex.value = deleteIndex - 1
|
||||||
await delete_logs_by_filename(
|
await delete_logs_by_filename(
|
||||||
props.instance.path,
|
props.instance.path,
|
||||||
|
|||||||
@@ -284,7 +284,7 @@
|
|||||||
:link-function="(page) => `?page=${page}`"
|
:link-function="(page) => `?page=${page}`"
|
||||||
@switch-page="switchPage"
|
@switch-page="switchPage"
|
||||||
/>
|
/>
|
||||||
<Modal ref="deleteWarning" header="Are you sure?">
|
<ModalWrapper ref="deleteWarning" header="Are you sure?">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="markdown-body">
|
<div class="markdown-body">
|
||||||
<p>
|
<p>
|
||||||
@@ -302,8 +302,8 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
<Modal ref="deleteDisabledWarning" header="Are you sure?">
|
<ModalWrapper ref="deleteDisabledWarning" header="Are you sure?">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="markdown-body">
|
<div class="markdown-body">
|
||||||
<p>
|
<p>
|
||||||
@@ -325,8 +325,8 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
<ShareModal
|
<ShareModalWrapper
|
||||||
ref="shareModal"
|
ref="shareModal"
|
||||||
share-title="Sharing modpack content"
|
share-title="Sharing modpack content"
|
||||||
share-text="Check out the projects I'm using in my modpack!"
|
share-text="Check out the projects I'm using in my modpack!"
|
||||||
@@ -360,8 +360,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
Pagination,
|
Pagination,
|
||||||
DropdownSelect,
|
DropdownSelect,
|
||||||
ShareModal,
|
|
||||||
Modal,
|
|
||||||
Checkbox,
|
Checkbox,
|
||||||
AnimatedLogo,
|
AnimatedLogo,
|
||||||
Avatar,
|
Avatar,
|
||||||
@@ -379,8 +377,7 @@ import {
|
|||||||
update_project,
|
update_project,
|
||||||
} from '@/helpers/profile.js'
|
} from '@/helpers/profile.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
import { listen } from '@tauri-apps/api/event'
|
|
||||||
import { highlightModInProfile } from '@/helpers/utils.js'
|
import { highlightModInProfile } from '@/helpers/utils.js'
|
||||||
import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage, PackageIcon } from '@/assets/icons'
|
import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage, PackageIcon } from '@/assets/icons'
|
||||||
import ExportModal from '@/components/ui/ExportModal.vue'
|
import ExportModal from '@/components/ui/ExportModal.vue'
|
||||||
@@ -393,6 +390,9 @@ import {
|
|||||||
get_version_many,
|
get_version_many,
|
||||||
} from '@/helpers/cache.js'
|
} from '@/helpers/cache.js'
|
||||||
import { profile_listener } from '@/helpers/events.js'
|
import { profile_listener } from '@/helpers/events.js'
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
|
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
|
||||||
|
import { getCurrentWebview } from '@tauri-apps/api/webview'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instance: {
|
instance: {
|
||||||
@@ -682,7 +682,7 @@ const updateAll = async () => {
|
|||||||
projects.value[project].updating = false
|
projects.value[project].updating = false
|
||||||
}
|
}
|
||||||
|
|
||||||
mixpanel_track('InstanceUpdateAll', {
|
trackEvent('InstanceUpdateAll', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
count: setProjects.length,
|
count: setProjects.length,
|
||||||
@@ -708,7 +708,7 @@ const updateProject = async (mod) => {
|
|||||||
mod.version = mod.updateVersion.version_number
|
mod.version = mod.updateVersion.version_number
|
||||||
mod.updateVersion = null
|
mod.updateVersion = null
|
||||||
|
|
||||||
mixpanel_track('InstanceProjectUpdate', {
|
trackEvent('InstanceProjectUpdate', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
@@ -717,7 +717,7 @@ const updateProject = async (mod) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let locks = {}
|
const locks = {}
|
||||||
|
|
||||||
const toggleDisableMod = async (mod) => {
|
const toggleDisableMod = async (mod) => {
|
||||||
// Use mod's id as the key for the lock. If mod doesn't have a unique id, replace `mod.id` with some unique property.
|
// Use mod's id as the key for the lock. If mod doesn't have a unique id, replace `mod.id` with some unique property.
|
||||||
@@ -725,7 +725,7 @@ const toggleDisableMod = async (mod) => {
|
|||||||
locks[mod.id] = ref(null)
|
locks[mod.id] = ref(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
let lock = locks[mod.id]
|
const lock = locks[mod.id]
|
||||||
|
|
||||||
while (lock.value) {
|
while (lock.value) {
|
||||||
await lock.value
|
await lock.value
|
||||||
@@ -735,7 +735,7 @@ const toggleDisableMod = async (mod) => {
|
|||||||
.then((newPath) => {
|
.then((newPath) => {
|
||||||
mod.path = newPath
|
mod.path = newPath
|
||||||
mod.disabled = !mod.disabled
|
mod.disabled = !mod.disabled
|
||||||
mixpanel_track('InstanceProjectDisable', {
|
trackEvent('InstanceProjectDisable', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
@@ -756,7 +756,7 @@ const removeMod = async (mod) => {
|
|||||||
await remove_project(props.instance.path, mod.path).catch(handleError)
|
await remove_project(props.instance.path, mod.path).catch(handleError)
|
||||||
projects.value = projects.value.filter((x) => mod.path !== x.path)
|
projects.value = projects.value.filter((x) => mod.path !== x.path)
|
||||||
|
|
||||||
mixpanel_track('InstanceProjectRemove', {
|
trackEvent('InstanceProjectRemove', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
@@ -784,6 +784,7 @@ const deleteDisabled = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const shareNames = async () => {
|
const shareNames = async () => {
|
||||||
|
console.log(functionValues.value)
|
||||||
await shareModal.value.show(functionValues.value.map((x) => x.name).join('\n'))
|
await shareModal.value.show(functionValues.value.map((x) => x.name).join('\n'))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -878,8 +879,10 @@ async function refreshProjects() {
|
|||||||
refreshingProjects.value = false
|
refreshingProjects.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const unlisten = await listen('tauri://file-drop', async (event) => {
|
const unlisten = await getCurrentWebview().onDragDropEvent(async (event) => {
|
||||||
for (const file of event.payload) {
|
if (event.payload.type !== 'drop') return
|
||||||
|
|
||||||
|
for (const file of event.payload.paths) {
|
||||||
if (file.endsWith('.mrpack')) continue
|
if (file.endsWith('.mrpack')) continue
|
||||||
await add_project_from_path(props.instance.path, file).catch(handleError)
|
await add_project_from_path(props.instance.path, file).catch(handleError)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModalWrapper
|
||||||
ref="modal_confirm"
|
ref="modal_confirm"
|
||||||
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."
|
||||||
:has-to-type="false"
|
:has-to-type="false"
|
||||||
proceed-label="Delete"
|
proceed-label="Delete"
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
@proceed="removeProfile"
|
@proceed="removeProfile"
|
||||||
/>
|
/>
|
||||||
<Modal
|
<ModalWrapper ref="modalConfirmUnlock" header="Are you sure you want to unlock this instance?">
|
||||||
ref="modalConfirmUnlock"
|
|
||||||
header="Are you sure you want to unlock this instance?"
|
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
>
|
|
||||||
<div class="modal-delete">
|
<div class="modal-delete">
|
||||||
<div
|
<div
|
||||||
class="markdown-body"
|
class="markdown-body"
|
||||||
@@ -31,13 +26,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
|
|
||||||
<Modal
|
<ModalWrapper ref="modalConfirmUnpair" header="Are you sure you want to unpair this instance?">
|
||||||
ref="modalConfirmUnpair"
|
|
||||||
header="Are you sure you want to unpair this instance?"
|
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
>
|
|
||||||
<div class="modal-delete">
|
<div class="modal-delete">
|
||||||
<div
|
<div
|
||||||
class="markdown-body"
|
class="markdown-body"
|
||||||
@@ -56,13 +47,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
|
|
||||||
<Modal
|
<ModalWrapper ref="changeVersionsModal" header="Change instance versions">
|
||||||
ref="changeVersionsModal"
|
|
||||||
header="Change instance versions"
|
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
>
|
|
||||||
<div class="change-versions-modal universal-body">
|
<div class="change-versions-modal universal-body">
|
||||||
<div class="input-row">
|
<div class="input-row">
|
||||||
<p class="input-label">Loader</p>
|
<p class="input-label">Loader</p>
|
||||||
@@ -106,7 +93,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</ModalWrapper>
|
||||||
<section class="card">
|
<section class="card">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<h3>
|
<h3>
|
||||||
@@ -511,18 +498,7 @@ import {
|
|||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
ClipboardCopyIcon,
|
ClipboardCopyIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import {
|
import { Button, Toggle, Card, Slider, Checkbox, Avatar, Chips, DropdownSelect } from '@modrinth/ui'
|
||||||
Button,
|
|
||||||
Toggle,
|
|
||||||
ConfirmModal,
|
|
||||||
Card,
|
|
||||||
Slider,
|
|
||||||
Checkbox,
|
|
||||||
Avatar,
|
|
||||||
Modal,
|
|
||||||
Chips,
|
|
||||||
DropdownSelect,
|
|
||||||
} from '@modrinth/ui'
|
|
||||||
import { SwapIcon } from '@/assets/icons'
|
import { SwapIcon } from '@/assets/icons'
|
||||||
|
|
||||||
import { Multiselect } from 'vue-multiselect'
|
import { Multiselect } from 'vue-multiselect'
|
||||||
@@ -541,15 +517,16 @@ import { computed, readonly, ref, shallowRef, watch } from 'vue'
|
|||||||
import { get_max_memory } from '@/helpers/jre.js'
|
import { get_max_memory } from '@/helpers/jre.js'
|
||||||
import { get } from '@/helpers/settings.js'
|
import { get } from '@/helpers/settings.js'
|
||||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/plugin-dialog'
|
||||||
import { get_loader_versions } from '@/helpers/metadata.js'
|
import { get_loader_versions } from '@/helpers/metadata.js'
|
||||||
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
|
||||||
import { useTheming } from '@/store/theme.js'
|
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
|
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
|
||||||
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
|
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
|
||||||
|
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
@@ -570,8 +547,6 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
const title = ref(props.instance.name)
|
const title = ref(props.instance.name)
|
||||||
const icon = ref(props.instance.icon_path)
|
const icon = ref(props.instance.icon_path)
|
||||||
const groups = ref(props.instance.groups)
|
const groups = ref(props.instance.groups)
|
||||||
@@ -590,7 +565,7 @@ const availableGroups = ref([
|
|||||||
async function resetIcon() {
|
async function resetIcon() {
|
||||||
icon.value = null
|
icon.value = null
|
||||||
await edit_icon(props.instance.path, null).catch(handleError)
|
await edit_icon(props.instance.path, null).catch(handleError)
|
||||||
mixpanel_track('InstanceRemoveIcon')
|
trackEvent('InstanceRemoveIcon')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setIcon() {
|
async function setIcon() {
|
||||||
@@ -606,10 +581,10 @@ async function setIcon() {
|
|||||||
|
|
||||||
if (!value) return
|
if (!value) return
|
||||||
|
|
||||||
icon.value = value
|
icon.value = value.path ?? value
|
||||||
await edit_icon(props.instance.path, icon.value).catch(handleError)
|
await edit_icon(props.instance.path, icon.value).catch(handleError)
|
||||||
|
|
||||||
mixpanel_track('InstanceSetIcon')
|
trackEvent('InstanceSetIcon')
|
||||||
}
|
}
|
||||||
|
|
||||||
const globalSettings = await get().catch(handleError)
|
const globalSettings = await get().catch(handleError)
|
||||||
@@ -621,12 +596,12 @@ const overrideJavaInstall = ref(!!props.instance.java_path)
|
|||||||
const optimalJava = readonly(await get_optimal_jre_key(props.instance.path).catch(handleError))
|
const optimalJava = readonly(await get_optimal_jre_key(props.instance.path).catch(handleError))
|
||||||
const javaInstall = ref({ path: optimalJava.path ?? props.instance.java_path })
|
const javaInstall = ref({ path: optimalJava.path ?? props.instance.java_path })
|
||||||
|
|
||||||
const overrideJavaArgs = ref(!!props.instance.extra_launch_args)
|
const overrideJavaArgs = ref(props.instance.extra_launch_args?.length !== undefined)
|
||||||
const javaArgs = ref(
|
const javaArgs = ref(
|
||||||
(props.instance.extra_launch_args ?? globalSettings.extra_launch_args).join(' '),
|
(props.instance.extra_launch_args ?? globalSettings.extra_launch_args).join(' '),
|
||||||
)
|
)
|
||||||
|
|
||||||
const overrideEnvVars = ref(!!props.instance.custom_env_vars)
|
const overrideEnvVars = ref(props.instance.custom_env_vars?.length !== undefined)
|
||||||
const envVars = ref(
|
const envVars = ref(
|
||||||
(props.instance.custom_env_vars ?? globalSettings.custom_env_vars)
|
(props.instance.custom_env_vars ?? globalSettings.custom_env_vars)
|
||||||
.map((x) => x.join('='))
|
.map((x) => x.join('='))
|
||||||
@@ -710,19 +685,15 @@ const editProfileObject = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (overrideJavaArgs.value) {
|
if (overrideJavaArgs.value) {
|
||||||
if (javaArgs.value !== '') {
|
editProfile.extra_launch_args = javaArgs.value.trim().split(/\s+/).filter(Boolean)
|
||||||
editProfile.extra_launch_args = javaArgs.value.trim().split(/\s+/).filter(Boolean)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideEnvVars.value) {
|
if (overrideEnvVars.value) {
|
||||||
if (envVars.value !== '') {
|
editProfile.custom_env_vars = envVars.value
|
||||||
editProfile.custom_env_vars = envVars.value
|
.trim()
|
||||||
.trim()
|
.split(/\s+/)
|
||||||
.split(/\s+/)
|
.filter(Boolean)
|
||||||
.filter(Boolean)
|
.map((x) => x.split('=').filter(Boolean))
|
||||||
.map((x) => x.split('=').filter(Boolean))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideMemorySettings.value) {
|
if (overrideMemorySettings.value) {
|
||||||
@@ -754,7 +725,7 @@ const repairing = ref(false)
|
|||||||
|
|
||||||
async function duplicateProfile() {
|
async function duplicateProfile() {
|
||||||
await duplicate(props.instance.path).catch(handleError)
|
await duplicate(props.instance.path).catch(handleError)
|
||||||
mixpanel_track('InstanceDuplicate', {
|
trackEvent('InstanceDuplicate', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
@@ -765,7 +736,7 @@ async function repairProfile(force) {
|
|||||||
await install(props.instance.path, force).catch(handleError)
|
await install(props.instance.path, force).catch(handleError)
|
||||||
repairing.value = false
|
repairing.value = false
|
||||||
|
|
||||||
mixpanel_track('InstanceRepair', {
|
trackEvent('InstanceRepair', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
@@ -796,7 +767,7 @@ async function repairModpack() {
|
|||||||
await update_repair_modrinth(props.instance.path).catch(handleError)
|
await update_repair_modrinth(props.instance.path).catch(handleError)
|
||||||
inProgress.value = false
|
inProgress.value = false
|
||||||
|
|
||||||
mixpanel_track('InstanceRepair', {
|
trackEvent('InstanceRepair', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
@@ -808,7 +779,7 @@ async function removeProfile() {
|
|||||||
await remove(props.instance.path).catch(handleError)
|
await remove(props.instance.path).catch(handleError)
|
||||||
removing.value = false
|
removing.value = false
|
||||||
|
|
||||||
mixpanel_track('InstanceRemove', {
|
trackEvent('InstanceRemove', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
@@ -905,7 +876,7 @@ const editing = ref(false)
|
|||||||
async function saveGvLoaderEdits() {
|
async function saveGvLoaderEdits() {
|
||||||
editing.value = true
|
editing.value = true
|
||||||
|
|
||||||
let editProfile = editProfileObject.value
|
const editProfile = editProfileObject.value
|
||||||
editProfile.loader = loader.value
|
editProfile.loader = loader.value
|
||||||
editProfile.game_version = gameVersion.value
|
editProfile.game_version = gameVersion.value
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,14 @@
|
|||||||
</span>
|
</span>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="expandedGalleryItem" class="expanded-image-modal" @click="expandedGalleryItem = null">
|
<div v-if="expandedGalleryItem" class="expanded-image-modal" @click="hideImage">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<img
|
<img
|
||||||
class="image"
|
class="image"
|
||||||
:class="{ 'zoomed-in': zoomedIn }"
|
:class="{ 'zoomed-in': zoomedIn }"
|
||||||
:src="
|
:src="
|
||||||
expandedGalleryItem.url
|
expandedGalleryItem.raw_url
|
||||||
? expandedGalleryItem.url
|
? expandedGalleryItem.raw_url
|
||||||
: 'https://cdn.modrinth.com/placeholder-banner.svg'
|
: 'https://cdn.modrinth.com/placeholder-banner.svg'
|
||||||
"
|
"
|
||||||
:alt="expandedGalleryItem.title ? expandedGalleryItem.title : 'gallery-image'"
|
:alt="expandedGalleryItem.title ? expandedGalleryItem.title : 'gallery-image'"
|
||||||
@@ -45,15 +45,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<Button class="close" icon-only @click="expandedGalleryItem = null">
|
<Button class="close" icon-only @click="hideImage">
|
||||||
<XIcon aria-hidden="true" />
|
<XIcon aria-hidden="true" />
|
||||||
</Button>
|
</Button>
|
||||||
<a
|
<a
|
||||||
class="open btn icon-only"
|
class="open btn icon-only"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
:href="
|
:href="
|
||||||
expandedGalleryItem.url
|
expandedGalleryItem.raw_url
|
||||||
? expandedGalleryItem.url
|
? expandedGalleryItem.raw_url
|
||||||
: 'https://cdn.modrinth.com/placeholder-banner.svg'
|
: 'https://cdn.modrinth.com/placeholder-banner.svg'
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@@ -93,7 +93,8 @@ import {
|
|||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Button, Card } from '@modrinth/ui'
|
import { Button, Card } from '@modrinth/ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
|
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
project: {
|
project: {
|
||||||
@@ -102,9 +103,14 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
let expandedGalleryItem = ref(null)
|
const expandedGalleryItem = ref(null)
|
||||||
let expandedGalleryIndex = ref(0)
|
const expandedGalleryIndex = ref(0)
|
||||||
let zoomedIn = ref(false)
|
const zoomedIn = ref(false)
|
||||||
|
|
||||||
|
const hideImage = () => {
|
||||||
|
expandedGalleryItem.value = null
|
||||||
|
show_ads_window()
|
||||||
|
}
|
||||||
|
|
||||||
const nextImage = () => {
|
const nextImage = () => {
|
||||||
expandedGalleryIndex.value++
|
expandedGalleryIndex.value++
|
||||||
@@ -112,7 +118,7 @@ const nextImage = () => {
|
|||||||
expandedGalleryIndex.value = 0
|
expandedGalleryIndex.value = 0
|
||||||
}
|
}
|
||||||
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
||||||
mixpanel_track('GalleryImageNext', {
|
trackEvent('GalleryImageNext', {
|
||||||
project_id: props.project.id,
|
project_id: props.project.id,
|
||||||
url: expandedGalleryItem.value.url,
|
url: expandedGalleryItem.value.url,
|
||||||
})
|
})
|
||||||
@@ -124,22 +130,37 @@ const previousImage = () => {
|
|||||||
expandedGalleryIndex.value = props.project.gallery.length - 1
|
expandedGalleryIndex.value = props.project.gallery.length - 1
|
||||||
}
|
}
|
||||||
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
||||||
mixpanel_track('GalleryImagePrevious', {
|
trackEvent('GalleryImagePrevious', {
|
||||||
project_id: props.project.id,
|
project_id: props.project.id,
|
||||||
url: expandedGalleryItem.value,
|
url: expandedGalleryItem.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const expandImage = (item, index) => {
|
const expandImage = (item, index) => {
|
||||||
|
hide_ads_window()
|
||||||
expandedGalleryItem.value = item
|
expandedGalleryItem.value = item
|
||||||
expandedGalleryIndex.value = index
|
expandedGalleryIndex.value = index
|
||||||
zoomedIn.value = false
|
zoomedIn.value = false
|
||||||
|
|
||||||
mixpanel_track('GalleryImageExpand', {
|
trackEvent('GalleryImageExpand', {
|
||||||
project_id: props.project.id,
|
project_id: props.project.id,
|
||||||
url: item.url,
|
url: item.url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function keyListener(e) {
|
||||||
|
if (expandedGalleryItem.value) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
hideImage()
|
||||||
|
} else if (e.key === 'ArrowLeft') {
|
||||||
|
previousImage()
|
||||||
|
} else if (e.key === 'ArrowRight') {
|
||||||
|
nextImage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('keypress', keyListener)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="root-container">
|
<div class="root-container">
|
||||||
<div v-if="data" class="project-sidebar">
|
<div v-if="data" class="project-sidebar" @scroll="$refs.promo.scroll()">
|
||||||
<Card v-if="instance" class="small-instance">
|
<Card v-if="instance" class="small-instance">
|
||||||
<router-link class="instance" :to="`/instance/${encodeURIComponent(instance.path)}`">
|
<router-link class="instance" :to="`/instance/${encodeURIComponent(instance.path)}`">
|
||||||
<Avatar
|
<Avatar
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</Card>
|
</Card>
|
||||||
<Card class="sidebar-card" @contextmenu.prevent.stop="handleRightClick">
|
<Card class="sidebar-card" @contextmenu.prevent.stop="handleRightClick">
|
||||||
<Avatar size="lg" :src="data.icon_url" />
|
<Avatar size="md" :src="data.icon_url" />
|
||||||
<div class="instance-info">
|
<div class="instance-info">
|
||||||
<h2 class="name">{{ data.title }}</h2>
|
<h2 class="name">{{ data.title }}</h2>
|
||||||
{{ data.description }}
|
{{ data.description }}
|
||||||
@@ -61,7 +61,9 @@
|
|||||||
Site
|
Site
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<hr class="card-divider" />
|
</Card>
|
||||||
|
<PromotionWrapper ref="promo" />
|
||||||
|
<Card class="sidebar-card">
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<DownloadIcon aria-hidden="true" />
|
<DownloadIcon aria-hidden="true" />
|
||||||
@@ -163,7 +165,6 @@
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="data" class="content-container">
|
<div v-if="data" class="content-container">
|
||||||
<PromotionWrapper />
|
|
||||||
<Card class="tabs">
|
<Card class="tabs">
|
||||||
<NavRow
|
<NavRow
|
||||||
v-if="data.gallery.length > 0"
|
v-if="data.gallery.length > 0"
|
||||||
@@ -231,15 +232,7 @@ import {
|
|||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
ClipboardCopyIcon,
|
ClipboardCopyIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import {
|
import { Categories, EnvironmentIndicator, Card, Avatar, Button, NavRow } from '@modrinth/ui'
|
||||||
Categories,
|
|
||||||
EnvironmentIndicator,
|
|
||||||
Card,
|
|
||||||
Avatar,
|
|
||||||
Button,
|
|
||||||
Promotion,
|
|
||||||
NavRow,
|
|
||||||
} from '@modrinth/ui'
|
|
||||||
import { formatNumber } from '@modrinth/utils'
|
import { formatNumber } from '@modrinth/utils'
|
||||||
import {
|
import {
|
||||||
BuyMeACoffeeIcon,
|
BuyMeACoffeeIcon,
|
||||||
@@ -257,10 +250,10 @@ import { useRoute } from 'vue-router'
|
|||||||
import { ref, shallowRef, watch } from 'vue'
|
import { ref, shallowRef, watch } from 'vue'
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import { install as installVersion } from '@/store/install.js'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
import { get_project, get_project_many, get_team, get_version_many } from '@/helpers/cache.js'
|
import { get_project, get_team, get_version_many } from '@/helpers/cache.js'
|
||||||
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
@@ -309,11 +302,13 @@ async function fetchProjectData() {
|
|||||||
|
|
||||||
await fetchProjectData()
|
await fetchProjectData()
|
||||||
|
|
||||||
|
const promo = ref(null)
|
||||||
watch(
|
watch(
|
||||||
() => route.params.id,
|
() => route.params.id,
|
||||||
async () => {
|
async () => {
|
||||||
if (route.params.id && route.path.startsWith('/project')) {
|
if (route.params.id && route.path.startsWith('/project')) {
|
||||||
await fetchProjectData()
|
await fetchProjectData()
|
||||||
|
promo.value.scroll()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -377,7 +372,7 @@ const handleOptionsClick = (args) => {
|
|||||||
|
|
||||||
.project-sidebar {
|
.project-sidebar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 20rem;
|
width: calc(300px + 1.5rem);
|
||||||
min-height: calc(100vh - 3.25rem);
|
min-height: calc(100vh - 3.25rem);
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
max-height: calc(100vh - 3.25rem);
|
max-height: calc(100vh - 3.25rem);
|
||||||
@@ -403,7 +398,7 @@ const handleOptionsClick = (args) => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin-left: 19.5rem;
|
margin-left: calc(300px + 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-group {
|
.button-group {
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export default new createRouter({
|
|||||||
linkExactActiveClass: 'router-link-exact-active',
|
linkExactActiveClass: 'router-link-exact-active',
|
||||||
scrollBehavior() {
|
scrollBehavior() {
|
||||||
// Sometimes Vue's scroll behavior is not working as expected, so we need to manually scroll to top (especially on Linux)
|
// Sometimes Vue's scroll behavior is not working as expected, so we need to manually scroll to top (especially on Linux)
|
||||||
document.querySelector('.router-view').scrollTop = 0
|
document.querySelector('.router-view')?.scrollTo(0, 0)
|
||||||
return {
|
return {
|
||||||
el: '.router-view',
|
el: '.router-view',
|
||||||
top: 0,
|
top: 0,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { get_project, get_version_many } from '@/helpers/cache.js'
|
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||||
import { install as packInstall } from '@/helpers/pack.js'
|
import { install as packInstall } from '@/helpers/pack.js'
|
||||||
import { mixpanel_track } from '@/helpers/mixpanel.js'
|
import { trackEvent } from '@/helpers/analytics.js'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
export const useInstall = defineStore('installStore', {
|
export const useInstall = defineStore('installStore', {
|
||||||
@@ -51,7 +51,7 @@ export const install = async (projectId, versionId, instancePath, source, callba
|
|||||||
if (packs.length === 0 || !packs.find((pack) => pack.linked_data?.project_id === project.id)) {
|
if (packs.length === 0 || !packs.find((pack) => pack.linked_data?.project_id === project.id)) {
|
||||||
await packInstall(project.id, version, project.title, project.icon_url).catch(handleError)
|
await packInstall(project.id, version, project.title, project.icon_url).catch(handleError)
|
||||||
|
|
||||||
mixpanel_track('PackInstall', {
|
trackEvent('PackInstall', {
|
||||||
id: project.id,
|
id: project.id,
|
||||||
version_id: version,
|
version_id: version,
|
||||||
title: project.title,
|
title: project.title,
|
||||||
@@ -107,7 +107,7 @@ export const install = async (projectId, versionId, instancePath, source, callba
|
|||||||
await add_project_from_version(instance.path, version.id).catch(handleError)
|
await add_project_from_version(instance.path, version.id).catch(handleError)
|
||||||
await installVersionDependencies(instance, version)
|
await installVersionDependencies(instance, version)
|
||||||
|
|
||||||
mixpanel_track('ProjectInstall', {
|
trackEvent('ProjectInstall', {
|
||||||
loader: instance.loader,
|
loader: instance.loader,
|
||||||
game_version: instance.game_version,
|
game_version: instance.game_version,
|
||||||
id: project.id,
|
id: project.id,
|
||||||
|
|||||||
@@ -23,7 +23,3 @@ export const handleError = (err) => {
|
|||||||
})
|
})
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handleMixpanelError = (err) => {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
|
|||||||
24
apps/app-frontend/tsconfig.app.json
Normal file
24
apps/app-frontend/tsconfig.app.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowJs": true,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
|
||||||
|
"strict": true,
|
||||||
|
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
@@ -1,12 +1,4 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"files": [],
|
||||||
"module": "NodeNext",
|
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
|
||||||
"moduleResolution": "NodeNext",
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["src/*"]
|
|
||||||
},
|
|
||||||
"target": "ESNext"
|
|
||||||
},
|
|
||||||
"exclude": ["node_modules", "dist"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
17
apps/app-frontend/tsconfig.node.json
Normal file
17
apps/app-frontend/tsconfig.node.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
@@ -42,8 +42,8 @@ export default defineConfig({
|
|||||||
port: 1420,
|
port: 1420,
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
// to make use of `TAURI_DEBUG` and other env variables
|
// to make use of `TAURI_ENV_DEBUG` and other env variables
|
||||||
// https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
|
// https://v2.tauri.app/reference/environment-variables/#tauri-cli-hook-commands
|
||||||
envPrefix: ['VITE_', 'TAURI_'],
|
envPrefix: ['VITE_', 'TAURI_'],
|
||||||
build: {
|
build: {
|
||||||
// Tauri supports es2021
|
// Tauri supports es2021
|
||||||
|
|||||||
@@ -10,14 +10,12 @@ theseus = { path = "../../packages/app-lib", features = ["cli"] }
|
|||||||
|
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tauri = { version = "1.7.1", features = ["shell-open"] }
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
webbrowser = "0.8.13"
|
webbrowser = "0.8.13"
|
||||||
dunce = "1.0.3"
|
dunce = "1.0.3"
|
||||||
|
|
||||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
uuid = { version = "1.1", features = ["serde", "v4"] }
|
uuid = { version = "1.1", features = ["serde", "v4"] }
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "@modrinth/app-playground",
|
"name": "@modrinth/app-playground",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cargo build --release",
|
"build": "cargo build --release",
|
||||||
"lint": "cargo fmt --check && cargo clippy -- -D warnings",
|
"lint": "cargo fmt --check && cargo clippy --all-targets --all-features -- -D warnings",
|
||||||
"fix": "cargo fmt && cargo clippy --fix",
|
"fix": "cargo fmt && cargo clippy --fix",
|
||||||
"dev": "cargo run",
|
"dev": "cargo run",
|
||||||
"test": "cargo test"
|
"test": "cargo test"
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "theseus_gui"
|
name = "theseus_gui"
|
||||||
version = "0.8.3-1"
|
version = "0.8.9"
|
||||||
description = "A Tauri App"
|
description = "The Modrinth App is a desktop application for managing your Minecraft mods"
|
||||||
authors = ["you"]
|
license = "GPL-3.0-only"
|
||||||
license = ""
|
repository = "https://github.com/modrinth/code/apps/app/"
|
||||||
repository = ""
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1.5.3", features = [] }
|
tauri-build = { version = "2.0.0-rc", features = ["codegen"] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
theseus = { path = "../../packages/app-lib", features = ["tauri"] }
|
theseus = { path = "../../packages/app-lib", features = ["tauri"] }
|
||||||
@@ -19,16 +16,19 @@ theseus = { path = "../../packages/app-lib", features = ["tauri"] }
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
tauri = { version = "1.7.1", features = [ "app-all", "devtools", "dialog", "dialog-confirm", "dialog-open", "macos-private-api", "os-all", "protocol-asset", "shell-open", "window-close", "window-create", "window-hide", "window-maximize", "window-minimize", "window-set-decorations", "window-show", "window-start-dragging", "window-unmaximize", "window-unminimize"] }
|
tauri = { version = "2.0.0-rc", features = ["devtools", "macos-private-api", "protocol-asset", "unstable"] }
|
||||||
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
tauri-plugin-window-state = "2.0.0-rc"
|
||||||
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
tauri-plugin-deep-link = "2.0.0-rc"
|
||||||
tauri-plugin-deep-link = "0.1.2"
|
tauri-plugin-os = "2.0.0-rc"
|
||||||
|
tauri-plugin-shell = "2.0.0-rc"
|
||||||
|
tauri-plugin-dialog = "2.0.0-rc"
|
||||||
|
tauri-plugin-updater = { version = "2.0.0-rc" }
|
||||||
|
tauri-plugin-single-instance = { version = "2.0.0-rc" }
|
||||||
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
daedalus = "0.2.3"
|
daedalus = { path = "../../packages/daedalus" }
|
||||||
chrono = "0.4.26"
|
chrono = "0.4.26"
|
||||||
|
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
@@ -56,6 +56,10 @@ window-shadows = "0.2.1"
|
|||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
cocoa = "0.25.0"
|
cocoa = "0.25.0"
|
||||||
objc = "0.2.7"
|
objc = "0.2.7"
|
||||||
|
rand = "0.8.5"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
tauri-plugin-updater = { version = "2.0.0-rc", optional = true, features = ["native-tls-vendored", "zip"], default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# by default Tauri runs in production mode
|
# by default Tauri runs in production mode
|
||||||
@@ -64,3 +68,4 @@ default = ["custom-protocol"]
|
|||||||
# this feature is used for production builds where `devPath` points to the filesystem
|
# this feature is used for production builds where `devPath` points to the filesystem
|
||||||
# DO NOT remove this
|
# DO NOT remove this
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
updater = []
|
||||||
|
|||||||
@@ -6,63 +6,17 @@
|
|||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLName</key>
|
<key>CFBundleURLName</key>
|
||||||
<!-- Obviously needs to be replaced with your app's bundle identifier -->
|
|
||||||
<string>ModrinthApp</string>
|
<string>ModrinthApp</string>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<!-- register the myapp:// and myscheme:// schemes -->
|
|
||||||
<string>modrinth</string>
|
<string>modrinth</string>
|
||||||
|
<string>modrinthscheme</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<!-- Declare file types your app can open -->
|
<key>NSCameraUsageDescription</key>
|
||||||
<key>CFBundleDocumentTypes</key>
|
<string>A Minecraft mod wants to access your camera.</string>
|
||||||
<array>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<dict>
|
<string>A Minecraft mod wants to access your microphone.</string>
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Modrinth type</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Owner</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.modrinth.theseus-type</string>
|
|
||||||
</array>
|
|
||||||
<key>NSDocumentClass</key>
|
|
||||||
<string>NSDocument</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>UTImportedTypeDeclarations</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Modrinth File</string>
|
|
||||||
<key>UTTypeIcons</key>
|
|
||||||
<dict/>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>com.modrinth.theseus-type</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>mrpack</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>application/x-mrpack</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>NSCameraUsageDescription</key>
|
|
||||||
<string>A Minecraft mod wants to access your camera.</string>
|
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
|
||||||
<string>A Minecraft mod wants to access your microphone.</string>
|
|
||||||
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Before you begin, ensure you have the following installed on your machine:
|
|||||||
- [Node.js](https://nodejs.org/en/)
|
- [Node.js](https://nodejs.org/en/)
|
||||||
- [pnpm](https://pnpm.io/)
|
- [pnpm](https://pnpm.io/)
|
||||||
- [Rust](https://www.rust-lang.org/tools/install)
|
- [Rust](https://www.rust-lang.org/tools/install)
|
||||||
- [Tauri](https://tauri.app/v1/guides/getting-started/prerequisites/#installing)
|
- [Tauri](https://v2.tauri.app/start/prerequisites/)
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,239 @@
|
|||||||
|
use tauri_build::{DefaultPermissionRule, InlinedPlugin};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Build the Tauri app
|
// Sadly, there is no better way to do it right now
|
||||||
tauri_build::build();
|
// You could try parsing source code here and detecting #[tauri::command]
|
||||||
|
// But I think it's not worth it
|
||||||
|
// https://github.com/tauri-apps/tauri/issues/10075
|
||||||
|
tauri_build::try_build(
|
||||||
|
tauri_build::Attributes::new()
|
||||||
|
.codegen(tauri_build::CodegenContext::new())
|
||||||
|
.plugin(
|
||||||
|
"auth",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"login",
|
||||||
|
"remove_user",
|
||||||
|
"get_default_user",
|
||||||
|
"set_default_user",
|
||||||
|
"get_users",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"cache",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"get_project",
|
||||||
|
"get_project_many",
|
||||||
|
"get_version",
|
||||||
|
"get_version_many",
|
||||||
|
"get_user",
|
||||||
|
"get_user_many",
|
||||||
|
"get_team",
|
||||||
|
"get_team_many",
|
||||||
|
"get_organization",
|
||||||
|
"get_organization_many",
|
||||||
|
"get_search_results",
|
||||||
|
"get_search_results_many",
|
||||||
|
"purge_cache_types",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"import",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"get_importable_instances",
|
||||||
|
"import_instance",
|
||||||
|
"is_valid_importable_instance",
|
||||||
|
"get_default_launcher_path",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"jre",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"get_java_versions",
|
||||||
|
"set_java_version",
|
||||||
|
"jre_find_filtered_jres",
|
||||||
|
"jre_get_jre",
|
||||||
|
"jre_test_jre",
|
||||||
|
"jre_auto_install_java",
|
||||||
|
"jre_get_max_memory",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"logs",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"logs_get_logs",
|
||||||
|
"logs_get_logs_by_filename",
|
||||||
|
"logs_get_output_by_filename",
|
||||||
|
"logs_delete_logs",
|
||||||
|
"logs_delete_logs_by_filename",
|
||||||
|
"logs_get_latest_log_cursor",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"metadata",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"metadata_get_game_versions",
|
||||||
|
"metadata_get_loader_versions",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"mr-auth",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"login_pass",
|
||||||
|
"login_2fa",
|
||||||
|
"create_account",
|
||||||
|
"logout",
|
||||||
|
"get",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"pack",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&["pack_install", "pack_get_profile_from_pack"])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"process",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"process_get_all",
|
||||||
|
"process_get_by_profile_path",
|
||||||
|
"process_kill",
|
||||||
|
"process_wait_for",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"profile",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"profile_remove",
|
||||||
|
"profile_get",
|
||||||
|
"profile_get_many",
|
||||||
|
"profile_get_projects",
|
||||||
|
"profile_get_optimal_jre_key",
|
||||||
|
"profile_get_full_path",
|
||||||
|
"profile_get_mod_full_path",
|
||||||
|
"profile_list",
|
||||||
|
"profile_check_installed",
|
||||||
|
"profile_install",
|
||||||
|
"profile_update_all",
|
||||||
|
"profile_update_project",
|
||||||
|
"profile_add_project_from_version",
|
||||||
|
"profile_add_project_from_path",
|
||||||
|
"profile_toggle_disable_project",
|
||||||
|
"profile_remove_project",
|
||||||
|
"profile_update_managed_modrinth_version",
|
||||||
|
"profile_repair_managed_modrinth",
|
||||||
|
"profile_run",
|
||||||
|
"profile_run_credentials",
|
||||||
|
"profile_kill",
|
||||||
|
"profile_edit",
|
||||||
|
"profile_edit_icon",
|
||||||
|
"profile_export_mrpack",
|
||||||
|
"profile_get_pack_export_candidates",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"profile-create",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&["profile_create", "profile_duplicate"])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"settings",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"settings_get",
|
||||||
|
"settings_set",
|
||||||
|
"cancel_directory_change",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"tags",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"tags_get_categories",
|
||||||
|
"tags_get_report_types",
|
||||||
|
"tags_get_loaders",
|
||||||
|
"tags_get_game_versions",
|
||||||
|
"tags_get_donation_platforms",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"utils",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"get_os",
|
||||||
|
"should_disable_mouseover",
|
||||||
|
"highlight_in_folder",
|
||||||
|
"open_path",
|
||||||
|
"show_launcher_logs_folder",
|
||||||
|
"progress_bars_list",
|
||||||
|
"get_opening_command",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.plugin(
|
||||||
|
"ads",
|
||||||
|
InlinedPlugin::new()
|
||||||
|
.commands(&[
|
||||||
|
"init_ads_window",
|
||||||
|
"hide_ads_window",
|
||||||
|
"scroll_ads_window",
|
||||||
|
"show_ads_window",
|
||||||
|
"record_ads_click",
|
||||||
|
"open_link",
|
||||||
|
"get_ads_personalization",
|
||||||
|
])
|
||||||
|
.default_permission(
|
||||||
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.expect("Failed to run tauri-build");
|
||||||
}
|
}
|
||||||
|
|||||||
14
apps/app/capabilities/ads.json
Normal file
14
apps/app/capabilities/ads.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"identifier": "ads",
|
||||||
|
"description": "",
|
||||||
|
"local": false,
|
||||||
|
"remote": {
|
||||||
|
"urls": ["https://modrinth.com/*", "http://localhost:3000/*"]
|
||||||
|
},
|
||||||
|
"webviews": [
|
||||||
|
"ads-window"
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"ads:default"
|
||||||
|
]
|
||||||
|
}
|
||||||
30
apps/app/capabilities/core.json
Normal file
30
apps/app/capabilities/core.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"identifier": "core",
|
||||||
|
"description": "",
|
||||||
|
"local": true,
|
||||||
|
"windows": [
|
||||||
|
"main"
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"core:default",
|
||||||
|
"core:path:default",
|
||||||
|
"core:event:default",
|
||||||
|
"core:window:default",
|
||||||
|
"core:app:default",
|
||||||
|
"core:resources:default",
|
||||||
|
"core:menu:default",
|
||||||
|
"core:tray:default",
|
||||||
|
"core:window:allow-create",
|
||||||
|
"core:window:allow-maximize",
|
||||||
|
"core:window:allow-toggle-maximize",
|
||||||
|
"core:window:allow-unmaximize",
|
||||||
|
"core:window:allow-minimize",
|
||||||
|
"core:window:allow-unminimize",
|
||||||
|
"core:window:allow-show",
|
||||||
|
"core:window:allow-hide",
|
||||||
|
"core:window:allow-close",
|
||||||
|
"core:window:allow-set-decorations",
|
||||||
|
"core:window:allow-start-dragging",
|
||||||
|
"core:webview:allow-set-webview-zoom"
|
||||||
|
]
|
||||||
|
}
|
||||||
39
apps/app/capabilities/plugins.json
Normal file
39
apps/app/capabilities/plugins.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"identifier": "plugins",
|
||||||
|
"description": "",
|
||||||
|
"local": true,
|
||||||
|
"windows": ["main"],
|
||||||
|
"permissions": [
|
||||||
|
"dialog:allow-open",
|
||||||
|
"dialog:allow-confirm",
|
||||||
|
"shell:allow-open",
|
||||||
|
"os:allow-platform",
|
||||||
|
"os:allow-version",
|
||||||
|
"os:allow-os-type",
|
||||||
|
"os:allow-family",
|
||||||
|
"os:allow-arch",
|
||||||
|
"os:allow-exe-extension",
|
||||||
|
"os:allow-locale",
|
||||||
|
"os:allow-hostname",
|
||||||
|
"deep-link:default",
|
||||||
|
"window-state:default",
|
||||||
|
"window-state:allow-restore-state",
|
||||||
|
"window-state:allow-save-window-state",
|
||||||
|
|
||||||
|
"auth:default",
|
||||||
|
"import:default",
|
||||||
|
"jre:default",
|
||||||
|
"logs:default",
|
||||||
|
"metadata:default",
|
||||||
|
"mr-auth:default",
|
||||||
|
"profile-create:default",
|
||||||
|
"pack:default",
|
||||||
|
"process:default",
|
||||||
|
"profile:default",
|
||||||
|
"cache:default",
|
||||||
|
"settings:default",
|
||||||
|
"tags:default",
|
||||||
|
"utils:default",
|
||||||
|
"ads:default"
|
||||||
|
]
|
||||||
|
}
|
||||||
11
apps/app/capabilities/updater.json
Normal file
11
apps/app/capabilities/updater.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"identifier": "updater",
|
||||||
|
"description": "",
|
||||||
|
"local": true,
|
||||||
|
"windows": [
|
||||||
|
"main"
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"updater:default"
|
||||||
|
]
|
||||||
|
}
|
||||||
1
apps/app/gen/schemas/acl-manifests.json
Normal file
1
apps/app/gen/schemas/acl-manifests.json
Normal file
File diff suppressed because one or more lines are too long
1
apps/app/gen/schemas/capabilities.json
Normal file
1
apps/app/gen/schemas/capabilities.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"ads":{"identifier":"ads","description":"","remote":{"urls":["https://modrinth.com/*","http://localhost:3000/*"]},"local":false,"webviews":["ads-window"],"permissions":["ads:default"]},"core":{"identifier":"core","description":"","local":true,"windows":["main"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-maximize","core:window:allow-toggle-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-start-dragging","core:webview:allow-set-webview-zoom"]},"plugins":{"identifier":"plugins","description":"","local":true,"windows":["main"],"permissions":["dialog:allow-open","dialog:allow-confirm","shell:allow-open","os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","deep-link:default","window-state:default","window-state:allow-restore-state","window-state:allow-save-window-state","auth:default","import:default","jre:default","logs:default","metadata:default","mr-auth:default","profile-create:default","pack:default","process:default","profile:default","cache:default","settings:default","tags:default","utils:default","ads:default"]},"updater":{"identifier":"updater","description":"","local":true,"windows":["main"],"permissions":["updater:default"]}}
|
||||||
3314
apps/app/gen/schemas/desktop-schema.json
Normal file
3314
apps/app/gen/schemas/desktop-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
4279
apps/app/gen/schemas/linux-schema.json
Normal file
4279
apps/app/gen/schemas/linux-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
3314
apps/app/gen/schemas/macOS-schema.json
Normal file
3314
apps/app/gen/schemas/macOS-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
3314
apps/app/gen/schemas/windows-schema.json
Normal file
3314
apps/app/gen/schemas/windows-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,346 +0,0 @@
|
|||||||
<?if $(sys.BUILDARCH)="x86"?>
|
|
||||||
<?define Win64 = "no" ?>
|
|
||||||
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
|
|
||||||
<?elseif $(sys.BUILDARCH)="x64"?>
|
|
||||||
<?define Win64 = "yes" ?>
|
|
||||||
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
|
|
||||||
<?else?>
|
|
||||||
<?error Unsupported value of sys.BUILDARCH=$(sys.BUILDARCH)?>
|
|
||||||
<?endif?>
|
|
||||||
|
|
||||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
|
||||||
<Product
|
|
||||||
Id="*"
|
|
||||||
Name="{{product_name}}"
|
|
||||||
UpgradeCode="{{upgrade_code}}"
|
|
||||||
Language="!(loc.TauriLanguage)"
|
|
||||||
Manufacturer="{{manufacturer}}"
|
|
||||||
Version="{{version}}">
|
|
||||||
|
|
||||||
<Package Id="*"
|
|
||||||
Keywords="Installer"
|
|
||||||
InstallerVersion="450"
|
|
||||||
Languages="0"
|
|
||||||
Compressed="yes"
|
|
||||||
InstallScope="perMachine"
|
|
||||||
SummaryCodepage="!(loc.TauriCodepage)"/>
|
|
||||||
|
|
||||||
<!-- https://docs.microsoft.com/en-us/windows/win32/msi/reinstallmode -->
|
|
||||||
<!-- reinstall all files; rewrite all registry entries; reinstall all shortcuts -->
|
|
||||||
<Property Id="REINSTALLMODE" Value="amus" />
|
|
||||||
|
|
||||||
{{#if allow_downgrades}}
|
|
||||||
<MajorUpgrade Schedule="afterInstallInitialize" AllowDowngrades="yes" />
|
|
||||||
{{else}}
|
|
||||||
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="!(loc.DowngradeErrorMessage)" AllowSameVersionUpgrades="yes" />
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<InstallExecuteSequence>
|
|
||||||
<RemoveShortcuts>Installed AND NOT UPGRADINGPRODUCTCODE</RemoveShortcuts>
|
|
||||||
</InstallExecuteSequence>
|
|
||||||
|
|
||||||
<Media Id="1" Cabinet="app.cab" EmbedCab="yes" />
|
|
||||||
|
|
||||||
{{#if banner_path}}
|
|
||||||
<WixVariable Id="WixUIBannerBmp" Value="{{banner_path}}" />
|
|
||||||
{{/if}}
|
|
||||||
{{#if dialog_image_path}}
|
|
||||||
<WixVariable Id="WixUIDialogBmp" Value="{{dialog_image_path}}" />
|
|
||||||
{{/if}}
|
|
||||||
{{#if license}}
|
|
||||||
<WixVariable Id="WixUILicenseRtf" Value="{{license}}" />
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<Icon Id="ProductIcon" SourceFile="{{icon_path}}"/>
|
|
||||||
<Property Id="ARPPRODUCTICON" Value="ProductIcon" />
|
|
||||||
<Property Id="ARPNOREPAIR" Value="yes" Secure="yes" /> <!-- Remove repair -->
|
|
||||||
<SetProperty Id="ARPNOMODIFY" Value="1" After="InstallValidate" Sequence="execute"/>
|
|
||||||
|
|
||||||
<!-- initialize with previous InstallDir -->
|
|
||||||
<Property Id="INSTALLDIR">
|
|
||||||
<RegistrySearch Id="PrevInstallDirReg" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw"/>
|
|
||||||
</Property>
|
|
||||||
|
|
||||||
<!-- launch app checkbox -->
|
|
||||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="!(loc.LaunchApp)" />
|
|
||||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>
|
|
||||||
<Property Id="WixShellExecTarget" Value="[!Path]" />
|
|
||||||
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
|
|
||||||
|
|
||||||
<UI>
|
|
||||||
<!-- launch app checkbox -->
|
|
||||||
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
|
|
||||||
|
|
||||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
|
|
||||||
|
|
||||||
{{#unless license}}
|
|
||||||
<!-- Skip license dialog -->
|
|
||||||
<Publish Dialog="WelcomeDlg"
|
|
||||||
Control="Next"
|
|
||||||
Event="NewDialog"
|
|
||||||
Value="InstallDirDlg"
|
|
||||||
Order="2">1</Publish>
|
|
||||||
<Publish Dialog="InstallDirDlg"
|
|
||||||
Control="Back"
|
|
||||||
Event="NewDialog"
|
|
||||||
Value="WelcomeDlg"
|
|
||||||
Order="2">1</Publish>
|
|
||||||
{{/unless}}
|
|
||||||
</UI>
|
|
||||||
|
|
||||||
<UIRef Id="WixUI_InstallDir" />
|
|
||||||
|
|
||||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
|
||||||
<Directory Id="DesktopFolder" Name="Desktop">
|
|
||||||
<Component Id="ApplicationShortcutDesktop" Guid="*">
|
|
||||||
<Shortcut Id="ApplicationDesktopShortcut" Name="{{product_name}}" Description="Runs {{product_name}}" Target="[!Path]" WorkingDirectory="INSTALLDIR" />
|
|
||||||
<RemoveFolder Id="DesktopFolder" On="uninstall" />
|
|
||||||
<RegistryValue Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="Desktop Shortcut" Type="integer" Value="1" KeyPath="yes" />
|
|
||||||
</Component>
|
|
||||||
</Directory>
|
|
||||||
<Directory Id="$(var.PlatformProgramFilesFolder)" Name="PFiles">
|
|
||||||
<Directory Id="INSTALLDIR" Name="{{product_name}}"/>
|
|
||||||
</Directory>
|
|
||||||
<Directory Id="ProgramMenuFolder">
|
|
||||||
<Directory Id="ApplicationProgramsFolder" Name="{{product_name}}"/>
|
|
||||||
</Directory>
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
<DirectoryRef Id="INSTALLDIR">
|
|
||||||
<Component Id="RegistryEntries" Guid="*">
|
|
||||||
<RegistryKey Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}">
|
|
||||||
<RegistryValue Name="InstallDir" Type="string" Value="[INSTALLDIR]" KeyPath="yes" />
|
|
||||||
</RegistryKey>
|
|
||||||
</Component>
|
|
||||||
|
|
||||||
<Component Id="Path" Guid="{{path_component_guid}}" Win64="$(var.Win64)">
|
|
||||||
<File Id="Path" Source="{{app_exe_source}}" KeyPath="yes" Checksum="yes"/>
|
|
||||||
<!-- THESEUS -->
|
|
||||||
<ProgId Id="theseus.mrpack.Document" Description="Modrinth File">
|
|
||||||
<Extension Id="mrpack" ContentType="application/mrpack">
|
|
||||||
<!-- no flags on argument, so we can hijack deep link library-->
|
|
||||||
<Verb Id="open" Command="Open" TargetFile="Path" Argument=""%1"" />
|
|
||||||
</Extension>
|
|
||||||
</ProgId>
|
|
||||||
<!-- /THESEUS -->
|
|
||||||
</Component>
|
|
||||||
{{#each binaries as |bin| ~}}
|
|
||||||
<Component Id="{{ bin.id }}" Guid="{{bin.guid}}" Win64="$(var.Win64)">
|
|
||||||
<File Id="Bin_{{ bin.id }}" Source="{{bin.path}}" KeyPath="yes"/>
|
|
||||||
</Component>
|
|
||||||
{{/each~}}
|
|
||||||
{{#if enable_elevated_update_task}}
|
|
||||||
<Component Id="UpdateTask" Guid="C492327D-9720-4CD5-8DB8-F09082AF44BE" Win64="$(var.Win64)">
|
|
||||||
<File Id="UpdateTask" Source="update.xml" KeyPath="yes" Checksum="yes"/>
|
|
||||||
</Component>
|
|
||||||
<Component Id="UpdateTaskInstaller" Guid="011F25ED-9BE3-50A7-9E9B-3519ED2B9932" Win64="$(var.Win64)">
|
|
||||||
<File Id="UpdateTaskInstaller" Source="install-task.ps1" KeyPath="yes" Checksum="yes"/>
|
|
||||||
</Component>
|
|
||||||
<Component Id="UpdateTaskUninstaller" Guid="D4F6CC3F-32DC-5FD0-95E8-782FFD7BBCE1" Win64="$(var.Win64)">
|
|
||||||
<File Id="UpdateTaskUninstaller" Source="uninstall-task.ps1" KeyPath="yes" Checksum="yes"/>
|
|
||||||
</Component>
|
|
||||||
{{/if}}
|
|
||||||
{{resources}}
|
|
||||||
<Component Id="CMP_UninstallShortcut" Guid="*">
|
|
||||||
|
|
||||||
<Shortcut Id="UninstallShortcut"
|
|
||||||
Name="Uninstall {{product_name}}"
|
|
||||||
Description="Uninstalls {{product_name}}"
|
|
||||||
Target="[System64Folder]msiexec.exe"
|
|
||||||
Arguments="/x [ProductCode]" />
|
|
||||||
|
|
||||||
<RemoveFolder Id="INSTALLDIR"
|
|
||||||
On="uninstall" />
|
|
||||||
|
|
||||||
<RegistryValue Root="HKCU"
|
|
||||||
Key="Software\\{{manufacturer}}\\{{product_name}}"
|
|
||||||
Name="Uninstaller Shortcut"
|
|
||||||
Type="integer"
|
|
||||||
Value="1"
|
|
||||||
KeyPath="yes" />
|
|
||||||
</Component>
|
|
||||||
|
|
||||||
<!-- THESEUS -->
|
|
||||||
<Component Id="FileTypeAssociationsReg" Guid="*">
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities" Name="ApplicationDescription" Value="theseus" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities" Name="ApplicationIcon" Value="[INSTALLDIR]theseus,0" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities" Name="ApplicationName" Value="theseus" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities\DefaultIcon" Value="[INSTALLDIR]theseus,1" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities\FileAssociations" Name=".mrpack" Value="theseus.mrpack.Document" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities\MIMEAssociations" Name="application/mrpack" Value="theseus.mrpack.Document" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities\shell\Open\command" Value=""[INSTALLDIR]theseus" -e "%1"" Type="string" />
|
|
||||||
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\RegisteredApplications" Name="theseus" Value="SOFTWARE\modrinth\theseus\Capabilities" Type="string" KeyPath="yes" />
|
|
||||||
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\theseus.mrpack.Document" Name="MRPACK File" Value="Modrinth Modpack Installer" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\.mrpack" Name="Content Type" Value="application/mrpack" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\.mrpack\OpenWithList\theseus" Value="" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\.mrpack\OpenWithProgids" Name="theseus.mrpack.Document" Value="" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\Applications\mrpack\SupportedTypes" Name=".mrpack" Value="" Type="string" />
|
|
||||||
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\Applications\mrpack\shell\open" Name="FriendlyAppName" Value="theseus" Type="string" />
|
|
||||||
</Component>
|
|
||||||
<!-- /THESEUS -->
|
|
||||||
|
|
||||||
</DirectoryRef>
|
|
||||||
|
|
||||||
<DirectoryRef Id="ApplicationProgramsFolder">
|
|
||||||
<Component Id="ApplicationShortcut" Guid="*">
|
|
||||||
<Shortcut Id="ApplicationStartMenuShortcut"
|
|
||||||
Name="{{product_name}}"
|
|
||||||
Description="Runs {{product_name}}"
|
|
||||||
Target="[!Path]"
|
|
||||||
Icon="ProductIcon"
|
|
||||||
WorkingDirectory="INSTALLDIR">
|
|
||||||
<ShortcutProperty Key="System.AppUserModel.ID" Value="{{bundle_id}}"/>
|
|
||||||
</Shortcut>
|
|
||||||
<RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
|
|
||||||
<RegistryValue Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="Start Menu Shortcut" Type="integer" Value="1" KeyPath="yes"/>
|
|
||||||
</Component>
|
|
||||||
</DirectoryRef>
|
|
||||||
|
|
||||||
{{#each merge_modules as |msm| ~}}
|
|
||||||
<DirectoryRef Id="TARGETDIR">
|
|
||||||
<Merge Id="{{ msm.name }}" SourceFile="{{ msm.path }}" DiskId="1" Language="!(loc.TauriLanguage)" />
|
|
||||||
</DirectoryRef>
|
|
||||||
|
|
||||||
<Feature Id="{{ msm.name }}" Title="{{ msm.name }}" AllowAdvertise="no" Display="hidden" Level="1">
|
|
||||||
<MergeRef Id="{{ msm.name }}"/>
|
|
||||||
</Feature>
|
|
||||||
{{/each~}}
|
|
||||||
|
|
||||||
<Feature
|
|
||||||
Id="MainProgram"
|
|
||||||
Title="Application"
|
|
||||||
Description="!(loc.InstallAppFeature)"
|
|
||||||
Level="1"
|
|
||||||
ConfigurableDirectory="INSTALLDIR"
|
|
||||||
AllowAdvertise="no"
|
|
||||||
Display="expand"
|
|
||||||
Absent="disallow">
|
|
||||||
|
|
||||||
<ComponentRef Id="RegistryEntries"/>
|
|
||||||
|
|
||||||
<!-- THESEUS -->
|
|
||||||
<ComponentRef Id="FileTypeAssociationsReg" />
|
|
||||||
<!-- /THESEUS -->
|
|
||||||
|
|
||||||
{{#each resource_file_ids as |resource_file_id| ~}}
|
|
||||||
<ComponentRef Id="{{ resource_file_id }}"/>
|
|
||||||
{{/each~}}
|
|
||||||
|
|
||||||
{{#if enable_elevated_update_task}}
|
|
||||||
<ComponentRef Id="UpdateTask" />
|
|
||||||
<ComponentRef Id="UpdateTaskInstaller" />
|
|
||||||
<ComponentRef Id="UpdateTaskUninstaller" />
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<Feature Id="ShortcutsFeature"
|
|
||||||
Title="Shortcuts"
|
|
||||||
Level="1">
|
|
||||||
<ComponentRef Id="Path"/>
|
|
||||||
<ComponentRef Id="CMP_UninstallShortcut" />
|
|
||||||
<ComponentRef Id="ApplicationShortcut" />
|
|
||||||
<ComponentRef Id="ApplicationShortcutDesktop" />
|
|
||||||
</Feature>
|
|
||||||
|
|
||||||
<Feature
|
|
||||||
Id="Environment"
|
|
||||||
Title="PATH Environment Variable"
|
|
||||||
Description="!(loc.PathEnvVarFeature)"
|
|
||||||
Level="1"
|
|
||||||
Absent="allow">
|
|
||||||
<ComponentRef Id="Path"/>
|
|
||||||
{{#each binaries as |bin| ~}}
|
|
||||||
<ComponentRef Id="{{ bin.id }}"/>
|
|
||||||
{{/each~}}
|
|
||||||
</Feature>
|
|
||||||
</Feature>
|
|
||||||
|
|
||||||
<Feature Id="External" AllowAdvertise="no" Absent="disallow">
|
|
||||||
{{#each component_group_refs as |id| ~}}
|
|
||||||
<ComponentGroupRef Id="{{ id }}"/>
|
|
||||||
{{/each~}}
|
|
||||||
{{#each component_refs as |id| ~}}
|
|
||||||
<ComponentRef Id="{{ id }}"/>
|
|
||||||
{{/each~}}
|
|
||||||
{{#each feature_group_refs as |id| ~}}
|
|
||||||
<FeatureGroupRef Id="{{ id }}"/>
|
|
||||||
{{/each~}}
|
|
||||||
{{#each feature_refs as |id| ~}}
|
|
||||||
<FeatureRef Id="{{ id }}"/>
|
|
||||||
{{/each~}}
|
|
||||||
{{#each merge_refs as |id| ~}}
|
|
||||||
<MergeRef Id="{{ id }}"/>
|
|
||||||
{{/each~}}
|
|
||||||
</Feature>
|
|
||||||
|
|
||||||
{{#if install_webview}}
|
|
||||||
<!-- WebView2 -->
|
|
||||||
<Property Id="WVRTINSTALLED">
|
|
||||||
<RegistrySearch Id="WVRTInstalledSystem" Root="HKLM" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw" Win64="no" />
|
|
||||||
<RegistrySearch Id="WVRTInstalledUser" Root="HKCU" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw"/>
|
|
||||||
</Property>
|
|
||||||
|
|
||||||
{{#if download_bootstrapper}}
|
|
||||||
<CustomAction Id='DownloadAndInvokeBootstrapper' Directory="INSTALLDIR" Execute="deferred" ExeCommand='powershell.exe -NoProfile -windowstyle hidden try [\{] [\[]Net.ServicePointManager[\]]::SecurityProtocol = [\[]Net.SecurityProtocolType[\]]::Tls12 [\}] catch [\{][\}]; Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" ; Start-Process -FilePath "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" -ArgumentList ({{webview_installer_args}} '/install') -Wait' Return='check'/>
|
|
||||||
<InstallExecuteSequence>
|
|
||||||
<Custom Action='DownloadAndInvokeBootstrapper' Before='InstallFinalize'>
|
|
||||||
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
|
||||||
</Custom>
|
|
||||||
</InstallExecuteSequence>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<!-- Embedded webview bootstrapper mode -->
|
|
||||||
{{#if webview2_bootstrapper_path}}
|
|
||||||
<Binary Id="MicrosoftEdgeWebview2Setup.exe" SourceFile="{{webview2_bootstrapper_path}}"/>
|
|
||||||
<CustomAction Id='InvokeBootstrapper' BinaryKey='MicrosoftEdgeWebview2Setup.exe' Execute="deferred" ExeCommand='{{webview_installer_args}} /install' Return='check' />
|
|
||||||
<InstallExecuteSequence>
|
|
||||||
<Custom Action='InvokeBootstrapper' Before='InstallFinalize'>
|
|
||||||
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
|
||||||
</Custom>
|
|
||||||
</InstallExecuteSequence>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<!-- Embedded offline installer -->
|
|
||||||
{{#if webview2_installer_path}}
|
|
||||||
<Binary Id="MicrosoftEdgeWebView2RuntimeInstaller.exe" SourceFile="{{webview2_installer_path}}"/>
|
|
||||||
<CustomAction Id='InvokeStandalone' BinaryKey='MicrosoftEdgeWebView2RuntimeInstaller.exe' Execute="deferred" ExeCommand='{{webview_installer_args}} /install' Return='check' />
|
|
||||||
<InstallExecuteSequence>
|
|
||||||
<Custom Action='InvokeStandalone' Before='InstallFinalize'>
|
|
||||||
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
|
||||||
</Custom>
|
|
||||||
</InstallExecuteSequence>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if enable_elevated_update_task}}
|
|
||||||
<!-- Install an elevated update task within Windows Task Scheduler -->
|
|
||||||
<CustomAction
|
|
||||||
Id="CreateUpdateTask"
|
|
||||||
Return="check"
|
|
||||||
Directory="INSTALLDIR"
|
|
||||||
Execute="commit"
|
|
||||||
Impersonate="yes"
|
|
||||||
ExeCommand="powershell.exe -WindowStyle hidden .\install-task.ps1" />
|
|
||||||
<InstallExecuteSequence>
|
|
||||||
<Custom Action='CreateUpdateTask' Before='InstallFinalize'>
|
|
||||||
NOT(REMOVE)
|
|
||||||
</Custom>
|
|
||||||
</InstallExecuteSequence>
|
|
||||||
<!-- Remove elevated update task during uninstall -->
|
|
||||||
<CustomAction
|
|
||||||
Id="DeleteUpdateTask"
|
|
||||||
Return="check"
|
|
||||||
Directory="INSTALLDIR"
|
|
||||||
ExeCommand="powershell.exe -WindowStyle hidden .\uninstall-task.ps1" />
|
|
||||||
<InstallExecuteSequence>
|
|
||||||
<Custom Action="DeleteUpdateTask" Before='InstallFinalize'>
|
|
||||||
(REMOVE = "ALL") AND NOT UPGRADINGPRODUCTCODE
|
|
||||||
</Custom>
|
|
||||||
</InstallExecuteSequence>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize"/>
|
|
||||||
</Product>
|
|
||||||
</Wix>
|
|
||||||
41
apps/app/nsis/hooks.nsi
Normal file
41
apps/app/nsis/hooks.nsi
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
!macro NSIS_HOOK_POSTINSTALL
|
||||||
|
SetShellVarContext current
|
||||||
|
|
||||||
|
IfFileExists "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe" file_found file_not_found
|
||||||
|
file_found:
|
||||||
|
Delete "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe"
|
||||||
|
|
||||||
|
Delete "$LOCALAPPDATA${PRODUCTNAME}\uninstall.exe"
|
||||||
|
RMDir "$LOCALAPPDATA${PRODUCTNAME}"
|
||||||
|
|
||||||
|
!insertmacro DeleteAppUserModelId
|
||||||
|
|
||||||
|
; Remove start menu shortcut
|
||||||
|
!insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder
|
||||||
|
!insertmacro IsShortcutTarget "$SMPROGRAMS$AppStartMenuFolder${PRODUCTNAME}.lnk" "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe"
|
||||||
|
Pop $0
|
||||||
|
${If} $0 = 1
|
||||||
|
!insertmacro UnpinShortcut "$SMPROGRAMS$AppStartMenuFolder${PRODUCTNAME}.lnk"
|
||||||
|
Delete "$SMPROGRAMS$AppStartMenuFolder${PRODUCTNAME}.lnk"
|
||||||
|
RMDir "$SMPROGRAMS$AppStartMenuFolder"
|
||||||
|
${EndIf}
|
||||||
|
!insertmacro IsShortcutTarget "$SMPROGRAMS${PRODUCTNAME}.lnk" "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe"
|
||||||
|
Pop $0
|
||||||
|
${If} $0 = 1
|
||||||
|
!insertmacro UnpinShortcut "$SMPROGRAMS${PRODUCTNAME}.lnk"
|
||||||
|
Delete "$SMPROGRAMS${PRODUCTNAME}.lnk"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
!insertmacro IsShortcutTarget "$DESKTOP${PRODUCTNAME}.lnk" "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe"
|
||||||
|
Pop $0
|
||||||
|
${If} $0 = 1
|
||||||
|
!insertmacro UnpinShortcut "$DESKTOP${PRODUCTNAME}.lnk"
|
||||||
|
Delete "$DESKTOP${PRODUCTNAME}.lnk"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
DeleteRegKey HKCU "${UNINSTKEY}"
|
||||||
|
|
||||||
|
goto end_of_test ;<== important for not continuing on the else branch
|
||||||
|
file_not_found:
|
||||||
|
end_of_test:
|
||||||
|
!macroend
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user