Compare commits
744 commits
main
...
immediate-
Author | SHA1 | Date | |
---|---|---|---|
7fcd9c3f06 | |||
8bfacc139a | |||
a9a8c338a6 | |||
262319a97c | |||
0379beb0ba | |||
13ada86934 | |||
fae5f9bfab | |||
7d4fc5e8e1 | |||
26a71caced | |||
b1ad0a0d58 | |||
4a9f258e34 | |||
301b0e7dfc | |||
2f806dde95 | |||
30ef53403b | |||
ade9a523ac | |||
464dddff13 | |||
e8965866e6 | |||
e3d9b2001f | |||
e4bc7f2add | |||
edc87ac75e | |||
5c7773c7bb | |||
3546632bbf | |||
9a48454a9f | |||
0e2c0b9bfb | |||
84c78a4262 | |||
cfbe4affc0 | |||
e8f017b15e | |||
22a81ab142 | |||
93d450a600 | |||
50bb6d867b | |||
6cc2f53501 | |||
f9776ce1ae | |||
18c5ca216c | |||
3f0d915c75 | |||
a8f77a12df | |||
44785d3498 | |||
473109b1d9 | |||
f398a1c871 | |||
9871e16e28 | |||
b22d4622ed | |||
f05d462a73 | |||
c183018fb1 | |||
f9c11a1149 | |||
45a876a0d5 | |||
4d88a13f5e | |||
eeb4d26812 | |||
2429c283aa | |||
1f1e3e8456 | |||
64e1f294e6 | |||
e801c71fdf | |||
4677ac779a | |||
ed32b7964d | |||
76f8b0e38e | |||
e24bdc2b6f | |||
4bcefb744d | |||
4d03a695aa | |||
ca3768ca5d | |||
6bdd0ba4a9 | |||
633a84581d | |||
4a590d3d0a | |||
1905aa0ae8 | |||
026e716710 | |||
9e6f24cc92 | |||
75a8098590 | |||
1f20074d4e | |||
fb394c6e1a | |||
465dd15e2c | |||
f676613173 | |||
be6209ee44 | |||
15a3694533 | |||
9fd16825e2 | |||
129b770ffa | |||
42353373db | |||
5f7c44f478 | |||
2df13a2e17 | |||
dc116fc3b1 | |||
cd3534f6fc | |||
01bd41413d | |||
654a271eae | |||
efd48e0364 | |||
09447d562e | |||
e57c99399d | |||
a3612dd4ee | |||
e9328cadea | |||
8fa19656ae | |||
d58d8aea71 | |||
5462bb1438 | |||
9dadde6884 | |||
81a1822e70 | |||
974953ba01 | |||
27fd143318 | |||
3bc71c688a | |||
d869b2ddbb | |||
fb3ae98419 | |||
70df451e64 | |||
2fb616ebf1 | |||
bd25e0cd75 | |||
62d4add6c1 | |||
3f5491d379 | |||
e35d0f680f | |||
187ece1905 | |||
2b99f4ece3 | |||
8bf98be472 | |||
0506505449 | |||
3edc36d5e0 | |||
6ffa969955 | |||
6213fca9ed | |||
56477d4e96 | |||
104f193a76 | |||
293b90a6d2 | |||
8159937827 | |||
aa35602827 | |||
42c4845261 | |||
710f627016 | |||
4389b78410 | |||
fde823fabf | |||
cce8f14d72 | |||
c8356570a3 | |||
021baa54d3 | |||
ddd5d9e0c3 | |||
920fb20d8e | |||
438b3e0598 | |||
763b24aa92 | |||
65013f0149 | |||
24795ba9f8 | |||
aa29e479b9 | |||
dd25044afd | |||
8c1979481e | |||
2a1684d902 | |||
b377cad1e6 | |||
4e09e347e2 | |||
ea6fccb54e | |||
c784bc5dbd | |||
edba931afe | |||
208882e8ec | |||
e0cb7242d6 | |||
b3ab32180c | |||
2506ba1b2e | |||
0d0c63f596 | |||
8abb68c553 | |||
f6147c6c13 | |||
b5fdb43cbf | |||
30057985c4 | |||
a95037d80a | |||
99e21ad284 | |||
d90bcdb063 | |||
771d51bcc3 | |||
c7e49672f5 | |||
3331ed2155 | |||
bc34d72b57 | |||
b333deb731 | |||
614cbc1146 | |||
a985286c88 | |||
1297408171 | |||
cc536bdbf4 | |||
ea859f59b0 | |||
a04ccd0d11 | |||
c1b4eb5b04 | |||
3f059e17be | |||
48442aee1d | |||
9ca13d1250 | |||
2c3be4533e | |||
7bf2152353 | |||
a6f4622837 | |||
2f55c7b126 | |||
14d1a2d1ea | |||
332f3e532b | |||
28a7fbb309 | |||
4caf86b60e | |||
5a85d6e78e | |||
20cfa932a4 | |||
9e998db456 | |||
3d4a7638a4 | |||
ab8da19e23 | |||
b8335108e4 | |||
403276d307 | |||
748a456cfb | |||
0cd0c813f2 | |||
f647ff0ee9 | |||
c2d708a621 | |||
35343bd790 | |||
cc5d4a7b3a | |||
ae655111de | |||
b768c61ef8 | |||
d95367b31d | |||
1e4ad4db4c | |||
d8ba959d1e | |||
0742cecd41 | |||
ed4a797ad3 | |||
505246954b | |||
eecc21db91 | |||
962eaa9df7 | |||
0190ada1a1 | |||
b5a1b3a124 | |||
1f3b8cdfd8 | |||
1f2c26cdba | |||
3b6bef1cda | |||
858b0e3dd7 | |||
718ebd56ac | |||
1511260666 | |||
4ff7c22505 | |||
057ccfde11 | |||
edf4e75ae2 | |||
62d9d1310d | |||
e592a50d7c | |||
c785053b86 | |||
e9081d2a52 | |||
98c67e1165 | |||
6c7d4259e1 | |||
d8e3381fb3 | |||
bdb9c75c74 | |||
a67b7847b1 | |||
bab081d4a9 | |||
0e5099416c | |||
20efa661b0 | |||
94ba593be9 | |||
c9295d3a85 | |||
d55770797a | |||
b4b67a98da | |||
453c0b62f4 | |||
a3fa74735f | |||
a3cab94a68 | |||
c4e7233604 | |||
889eb311f9 | |||
03120d5a98 | |||
9446efadd5 | |||
9c21c23364 | |||
ba6d3c7e60 | |||
cb1fff8d86 | |||
c400db41c3 | |||
de4743828a | |||
700a442ee4 | |||
dd3904badd | |||
0d360fb7e7 | |||
2429231965 | |||
570137040c | |||
ad3bb91bc1 | |||
161be30b2b | |||
0a4bcd5ccd | |||
3a5859b815 | |||
1ea5f56549 | |||
a7b02f2f1f | |||
32306cc6c3 | |||
8cfb289dc7 | |||
220062acdb | |||
e5d5bc2d34 | |||
17cc81f15d | |||
eba42489e9 | |||
51dee70ced | |||
11561e50f9 | |||
2095cc8d03 | |||
6e61ef297c | |||
58e810dfbe | |||
b2497373f0 | |||
18841f4551 | |||
da6ceb8479 | |||
6b811dce55 | |||
8982a53b47 | |||
3cf702a2ba | |||
cd79150a84 | |||
343e2802c3 | |||
dc8b579488 | |||
066001c57a | |||
24a0f62801 | |||
ef512e6ea1 | |||
c6b2eeb86d | |||
e457e58b49 | |||
cac0398276 | |||
e93562b95b | |||
f6dbcb7709 | |||
a82a0636cf | |||
2fc814124f | |||
a43d5d595a | |||
ceaaf9ab8a | |||
a01ee40591 | |||
26c2674652 | |||
9595f75a9a | |||
2189ce407b | |||
7b4f97fa08 | |||
f208472841 | |||
4e4a9f806f | |||
afe6d9f7b3 | |||
2dac66d7c6 | |||
83b09a8073 | |||
13d47d91bf | |||
004f764c2b | |||
a815122e3c | |||
b88e7faa72 | |||
bb22355a57 | |||
591f433135 | |||
f726376ab8 | |||
74bd89b721 | |||
aa4c6e5496 | |||
e8d696c18c | |||
d473d52047 | |||
36c8058920 | |||
8d31f3cf62 | |||
5021d2e344 | |||
92653eaf96 | |||
2f6e39c0bd | |||
5e52ecf42e | |||
b2ae33344b | |||
b24e8cb9e6 | |||
73b1c7b811 | |||
210fada19a | |||
002a1cfc16 | |||
e9e25306eb | |||
5931a72de2 | |||
3d0ce5449d | |||
31c39bc761 | |||
1d2b8a43c0 | |||
6d587e6530 | |||
853585c0bc | |||
6826fe003c | |||
c3bbc92fa2 | |||
ed35c496dc | |||
dc8ac98a6e | |||
337850c33a | |||
08f1b6a3c9 | |||
c8ad68c80b | |||
7b056364da | |||
7f3cad2528 | |||
5d290ecd35 | |||
b702b31744 | |||
3dbe621059 | |||
55195676b8 | |||
76cb49c381 | |||
95c4cc09af | |||
a5b30e762a | |||
5bbf467be0 | |||
46c771f351 | |||
94ea4f8e5c | |||
fc8a0e4904 | |||
9eb77cb7fd | |||
e1bbd67a9a | |||
625b248a95 | |||
94c365b1c3 | |||
f70e9290ce | |||
57b011aea7 | |||
c8097f0f1a | |||
5999af76c6 | |||
05717d6bc3 | |||
53dd86fa6e | |||
363f2e84fb | |||
066ce07512 | |||
f5b1b65db8 | |||
6e6ee4db68 | |||
ac1c417e1c | |||
4e14232b5a | |||
eddfca06ab | |||
61e1eada01 | |||
85322f9e90 | |||
d329d74fec | |||
ac6a21374f | |||
b385be51d6 | |||
d8524b087c | |||
c491122937 | |||
13337950dc | |||
281ab9194b | |||
a1881bff3d | |||
c50208b019 | |||
a8974f01eb | |||
b79a0dadcb | |||
c4c92ed366 | |||
78fd37a4c6 | |||
9de6e67cf1 | |||
33d5753ca7 | |||
8d3feef9a1 | |||
8580332c82 | |||
4d8c56689c | |||
5cbb816c1c | |||
b6594051d9 | |||
50415a949d | |||
4b03454bce | |||
f1b649e996 | |||
db5397f41a | |||
c34f40e30d | |||
dd4d80872e | |||
eb381767cd | |||
0aff3c29bc | |||
e7fdb3881a | |||
5977ff8f6b | |||
dd13b96728 | |||
01fdeb7292 | |||
6a3831c437 | |||
c485864ee8 | |||
2ab7d3209e | |||
654b2e8398 | |||
3f573c9af3 | |||
cbd9a9312c | |||
2656b60347 | |||
fbb7ee50dd | |||
4187932aa7 | |||
a19fb71c70 | |||
e21a6be4b6 | |||
b53c78c2ba | |||
db63fffe18 | |||
41ab8da5ad | |||
f68effc7e2 | |||
f21bfb9d78 | |||
f4cc95da5a | |||
3a32490937 | |||
545d69aa6d | |||
aa5ca42676 | |||
5197170b3d | |||
47eec049fc | |||
022a0497c3 | |||
e2f936538e | |||
b323fbb486 | |||
0c4e265530 | |||
111a4f2aa8 | |||
46710bf8cb | |||
1f938211a8 | |||
8d362fc394 | |||
79849fd37d | |||
a838d587ae | |||
3a25da5f14 | |||
a1e72e78d7 | |||
48fb6ecd1a | |||
3c62e393a3 | |||
034ef554fb | |||
2da9bf8f0e | |||
eb5fb0df29 | |||
479a4069b2 | |||
e22575805b | |||
c0984db612 | |||
9a9c1b0487 | |||
072619e539 | |||
9084459933 | |||
36f9eda3a8 | |||
99160638df | |||
2574d5311d | |||
7816727e26 | |||
892de53603 | |||
8de1ca86a9 | |||
01ba87cd76 | |||
83222631ab | |||
b74581908e | |||
896b8970b6 | |||
c8602ef52b | |||
ac1423f90c | |||
d0e4ec0ad5 | |||
7509f203cc | |||
17b575070f | |||
4a872236e1 | |||
d5440349c9 | |||
0f9b5f2d09 | |||
4db6c4b88a | |||
6e1094a5d1 | |||
15df3485d1 | |||
ea86df7174 | |||
66c466c9f0 | |||
96d5e1b89b | |||
4b4caa798a | |||
73503945fb | |||
43b679d676 | |||
011b375d02 | |||
9f1f2b15a4 | |||
a1309ea897 | |||
a4073703fd | |||
84b9e3fec1 | |||
6bbd91fafc | |||
d78f1c8000 | |||
64262001e8 | |||
6b648f3d38 | |||
1eeadbcd97 | |||
f016d277b7 | |||
9c2545ab16 | |||
b36d3b4385 | |||
acbc932542 | |||
3590d5abff | |||
c9c09b95a3 | |||
9128eb5760 | |||
513d9f4209 | |||
06393211b1 | |||
ac3130d37e | |||
3e2952890f | |||
a0751a0f84 | |||
fa45beb8ca | |||
539b70cd52 | |||
e96458fafa | |||
a1542bcec5 | |||
e00da485e8 | |||
513eea8d14 | |||
490e9131b5 | |||
9ceb0ae7cb | |||
6d59a943da | |||
00d71fbeab | |||
4459d11a97 | |||
1eab0146dd | |||
f345484e5b | |||
65adaf335c | |||
437e4eae44 | |||
f7d12d209c | |||
8a5678bec5 | |||
1f99162b5a | |||
8c5f2c897e | |||
9ada9bc1a9 | |||
8fd22f6deb | |||
c1e78b4397 | |||
32a2c90761 | |||
dae3841f10 | |||
4d27643d39 | |||
c2047e5f3b | |||
18142ecccb | |||
440cb1f29c | |||
64b1ab1112 | |||
3da88100df | |||
3e3201ad0d | |||
a2bda05211 | |||
1ed956114f | |||
2e27a513ac | |||
a8fa685cfa | |||
b2036762b5 | |||
067a16c9dc | |||
265cf12d39 | |||
b580d6dcfb | |||
affd527bc1 | |||
7d928e53aa | |||
b10afebc98 | |||
1a8a901e0a | |||
d936604f35 | |||
4dcc265be0 | |||
3e6e399544 | |||
9af63b362d | |||
090a3bc0a8 | |||
3331723496 | |||
999d6c812f | |||
c9c1e0c6c5 | |||
1e55ae5327 | |||
5b96881177 | |||
daa1a35fc3 | |||
ef895e787a | |||
2f16cdf77b | |||
e990795783 | |||
8dde496757 | |||
35f950c6e3 | |||
5fbabb0b70 | |||
ff9cc2e9e5 | |||
3793acd9bb | |||
ebae259f5d | |||
3296ee1c4b | |||
2f87363b89 | |||
bacf021a28 | |||
1138e22f58 | |||
d6b5bc58ce | |||
bd2264c125 | |||
86f69e84c1 | |||
488f133a90 | |||
77d8e70059 | |||
f64b228cb9 | |||
90f37471e5 | |||
b192c89146 | |||
ae10d3667c | |||
f727d101fc | |||
9bbec66ea5 | |||
0146789514 | |||
b38ab066d9 | |||
37514e0720 | |||
aa9be97f83 | |||
d8c6559967 | |||
72b014fe67 | |||
8eb4eab2b0 | |||
07361ff6fe | |||
64212512ca | |||
5e762ddd04 | |||
74f36a093b | |||
c01a8c2f78 | |||
90c26432f5 | |||
6f25005057 | |||
23c8818ed2 | |||
16d05c8935 | |||
c5541d297d | |||
a120b35f4f | |||
9740d28530 | |||
8e9434cdd5 | |||
f5be78d101 | |||
bd95cc449f | |||
bdd4b0f143 | |||
a60c8374ed | |||
1013c34840 | |||
7b7b87316d | |||
6567ff7435 | |||
22a75c4589 | |||
67b5ecca51 | |||
cdbd9ad101 | |||
1e96638219 | |||
7371abbaec | |||
cfb493c593 | |||
ac61a0377f | |||
bb3f629672 | |||
3ab68f9296 | |||
01a4ac9c13 | |||
66e1db1fdb | |||
688449ed62 | |||
5ca4e58fad | |||
deb0f7b9c6 | |||
40a1f48267 | |||
bc367b1d2a | |||
f6a7cdc430 | |||
d5bcbf54f9 | |||
4b2734610f | |||
c4645f79c6 | |||
cf9bbfc78f | |||
6cc40eb095 | |||
95acb147d1 | |||
21fea87551 | |||
2a2124c5a0 | |||
1a1e5c2e1d | |||
c9d9d53fb1 | |||
dc81d1c14c | |||
b938bdf138 | |||
e74aabb988 | |||
ab24ce08e3 | |||
c2435bb9dd | |||
d678e68899 | |||
dbb6f4165b | |||
9a1a7b374f | |||
ec373069b3 | |||
329bc68bf5 | |||
2a77941dff | |||
d36c361b47 | |||
b17963169f | |||
118361723c | |||
97e2e2c1c8 | |||
dbee2d9ecc | |||
1283494b46 | |||
e72c732d58 | |||
d40dfc345f | |||
70f60193b0 | |||
9e60d4979d | |||
3562c8ae11 | |||
093de43d54 | |||
86ad3bd4fc | |||
8045d65e99 | |||
63a1fe2c70 | |||
55760bc42b | |||
983013446d | |||
569dace244 | |||
8e5a2f6e59 | |||
41a1732df2 | |||
487af1c789 | |||
5168f326a3 | |||
4feaa40de7 | |||
7f5aaada88 | |||
725103a77c | |||
a878ea0b39 | |||
0cc89996c6 | |||
c72099fc39 | |||
84c223d471 | |||
82f9e36e5a | |||
7c8c2ff5b8 | |||
dc73b89b29 | |||
2596bf0990 | |||
7169dfe61a | |||
5ba5eca1a1 | |||
4209bfd3aa | |||
c007fc7478 | |||
903fa2cf11 | |||
7d91dea822 | |||
4fc4f1cb75 | |||
45d2cf49e0 | |||
b798f31669 | |||
7e1524362c | |||
e04dd85957 | |||
258e1efdce | |||
f6e484034c | |||
5676f45781 | |||
e11d651e33 | |||
09a6a9bf85 | |||
1b64419ef1 | |||
5edff4a3c5 | |||
b043d8abb7 | |||
e15868d499 | |||
a892a507be | |||
33a5f30ae4 | |||
38b6c7fa6c | |||
bd88625d25 | |||
5fca61f581 | |||
cc6cef02c1 | |||
546c5105b2 | |||
f65749e2d4 | |||
fb8f00137c | |||
20b071aa2d | |||
b7c1b495af | |||
4d80c53dd0 | |||
035e2add0b | |||
f70cc5205c | |||
fa948e8639 | |||
4cc6789b5b | |||
4753efa33c | |||
283a4b23ba | |||
3b7f1be9a2 | |||
c2e12e2827 | |||
8b6620ca2f | |||
f1e2d5572a | |||
49fcfd74df | |||
511ec1b129 | |||
94843a77ac | |||
8873391496 | |||
b77b654df1 | |||
c65e1f88d2 | |||
9cd20e3cea | |||
f04bd8a7ad | |||
fa82939e62 | |||
a1fefcf27a | |||
65b6440024 | |||
6e0064c5fc | |||
b047f9ce28 | |||
d97596a16a | |||
5b6d0f42fd | |||
4a89fadedd | |||
886071af4d | |||
12c9c324a1 | |||
e311bd9f76 | |||
707c23b188 | |||
49e78793c5 | |||
8d31330d3e | |||
17397dbe08 | |||
b98544ed1f | |||
043675f3f8 | |||
3b1cb02663 | |||
bc68bc0312 | |||
b3819228ed | |||
c23c65733b | |||
cfefdbc7a7 | |||
70103e8111 | |||
240705652c | |||
d42c9547ab | |||
ac85b3508b | |||
9ece4dddd3 | |||
36f5d9acc6 | |||
d4956f3ec7 | |||
8279c8d0ba | |||
74e3ba42e0 | |||
784ae0a02d | |||
901d803ba9 | |||
bdb693302a | |||
f8a7d1d634 | |||
770078f236 | |||
f1dd39885b | |||
a29115ba78 | |||
cd5e0aeec1 | |||
75fa0ff708 |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -18,5 +18,7 @@ lerna-debug.log*
|
|||
.pnpm-debug.log*
|
||||
*.tsbuildinfo
|
||||
|
||||
src/userplugins
|
||||
|
||||
ExtensionCache/
|
||||
settings/
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "src/userplugins/vc-message-logger-enhanced"]
|
||||
path = src/userplugins/vc-message-logger-enhanced
|
||||
url = https://github.com/Syncxv/vc-message-logger-enhanced.git
|
|
@ -5,7 +5,6 @@
|
|||
// @author Vendicated (https://github.com/Vendicated)
|
||||
// @namespace https://github.com/Vendicated/Vencord
|
||||
// @supportURL https://github.com/Vendicated/Vencord
|
||||
// @icon https://raw.githubusercontent.com/Vendicated/Vencord/refs/heads/main/browser/icon.png
|
||||
// @license GPL-3.0
|
||||
// @match *://*.discord.com/*
|
||||
// @grant GM_xmlhttpRequest
|
||||
|
|
|
@ -90,7 +90,13 @@ export default tseslint.config(
|
|||
"no-invalid-regexp": "error",
|
||||
"no-constant-condition": ["error", { "checkLoops": false }],
|
||||
"no-duplicate-imports": "error",
|
||||
"dot-notation": "error",
|
||||
"@typescript-eslint/dot-notation": [
|
||||
"error",
|
||||
{
|
||||
"allowPrivateClassPropertyAccess": true,
|
||||
"allowProtectedClassPropertyAccess": true
|
||||
}
|
||||
],
|
||||
"no-useless-escape": [
|
||||
"error",
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.10.7",
|
||||
"version": "1.9.8",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
@ -35,7 +35,6 @@
|
|||
"testTsc": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@intrnl/xxhash64": "^0.1.2",
|
||||
"@sapphi-red/web-noise-suppressor": "0.3.5",
|
||||
"@vap/core": "0.0.12",
|
||||
"@vap/shiki": "0.10.5",
|
||||
|
@ -71,7 +70,6 @@
|
|||
"stylelint": "^16.8.1",
|
||||
"stylelint-config-standard": "^36.0.1",
|
||||
"ts-patch": "^3.2.1",
|
||||
"ts-pattern": "^5.3.1",
|
||||
"tsx": "^4.16.5",
|
||||
"type-fest": "^4.23.0",
|
||||
"typescript": "^5.5.4",
|
||||
|
|
|
@ -16,9 +16,6 @@ importers:
|
|||
|
||||
.:
|
||||
dependencies:
|
||||
'@intrnl/xxhash64':
|
||||
specifier: ^0.1.2
|
||||
version: 0.1.2
|
||||
'@sapphi-red/web-noise-suppressor':
|
||||
specifier: 0.3.5
|
||||
version: 0.3.5
|
||||
|
@ -119,9 +116,6 @@ importers:
|
|||
ts-patch:
|
||||
specifier: ^3.2.1
|
||||
version: 3.2.1
|
||||
ts-pattern:
|
||||
specifier: ^5.3.1
|
||||
version: 5.3.1
|
||||
tsx:
|
||||
specifier: ^4.16.5
|
||||
version: 4.16.5
|
||||
|
@ -540,9 +534,6 @@ packages:
|
|||
resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@intrnl/xxhash64@0.1.2':
|
||||
resolution: {integrity: sha512-1+lx7j99fdph+uy3EnjQyr39KQZ7LP56+aWOr6finJWpgYpvb7XrhFUqDwnEk/wpPC98nCjAT6RulpW3crWjlg==}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.5':
|
||||
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
@ -2533,9 +2524,6 @@ packages:
|
|||
resolution: {integrity: sha512-hlR43v+GUIUy8/ZGFP1DquEqPh7PFKQdDMTAmYt671kCCA6AkDQMoeFaFmZ7ObPLYOmpMgyKUqL1C+coFMf30w==}
|
||||
hasBin: true
|
||||
|
||||
ts-pattern@5.3.1:
|
||||
resolution: {integrity: sha512-1RUMKa8jYQdNfmnK4jyzBK3/PS/tnjcZ1CW0v1vWDeYe5RBklc/nquw03MEoB66hVBm4BnlCfmOqDVxHyT1DpA==}
|
||||
|
||||
tsconfig-paths@3.15.0:
|
||||
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
|
||||
|
||||
|
@ -2945,8 +2933,6 @@ snapshots:
|
|||
|
||||
'@humanwhocodes/retry@0.3.0': {}
|
||||
|
||||
'@intrnl/xxhash64@0.1.2': {}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.5':
|
||||
dependencies:
|
||||
'@jridgewell/set-array': 1.2.1
|
||||
|
@ -5172,8 +5158,6 @@ snapshots:
|
|||
semver: 7.6.3
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
ts-pattern@5.3.1: {}
|
||||
|
||||
tsconfig-paths@3.15.0:
|
||||
dependencies:
|
||||
'@types/json5': 0.0.29
|
||||
|
|
|
@ -312,7 +312,7 @@ export const commonOpts = {
|
|||
logLevel: "info",
|
||||
bundle: true,
|
||||
watch,
|
||||
minify: !watch,
|
||||
minify: !watch && !IS_REPORTER,
|
||||
sourcemap: watch ? "inline" : "",
|
||||
legalComments: "linked",
|
||||
banner,
|
||||
|
@ -334,6 +334,5 @@ export const commonRendererPlugins = [
|
|||
banImportPlugin(builtinModuleRegex, "Cannot import node inbuilt modules in browser code. You need to use a native.ts file"),
|
||||
banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"),
|
||||
banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"),
|
||||
banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"),
|
||||
...commonOpts.plugins
|
||||
];
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
import { readFileSync } from "fs";
|
||||
import pup, { JSHandle } from "puppeteer-core";
|
||||
|
||||
for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) {
|
||||
for (const variable of ["CHROMIUM_BIN"]) {
|
||||
if (!process.env[variable]) {
|
||||
console.error(`Missing environment variable ${variable}`);
|
||||
process.exit(1);
|
||||
|
@ -214,7 +214,7 @@ page.on("console", async e => {
|
|||
|
||||
switch (tag) {
|
||||
case "WebpackInterceptor:":
|
||||
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!;
|
||||
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module|took [\d.]+?ms) \(Module id is (.+?)\): (.+)/)!;
|
||||
if (!patchFailMatch) break;
|
||||
|
||||
console.error(await getText());
|
||||
|
@ -225,7 +225,7 @@ page.on("console", async e => {
|
|||
plugin,
|
||||
type,
|
||||
id,
|
||||
match: regex.replace(/\(\?:\[A-Za-z_\$\]\[\\w\$\]\*\)/g, "\\i"),
|
||||
match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"),
|
||||
error: await maybeGetError(e.args()[3])
|
||||
});
|
||||
|
||||
|
@ -291,7 +291,7 @@ page.on("error", e => console.error("[Error]", e.message));
|
|||
page.on("pageerror", e => {
|
||||
if (e.message.includes("Sentry successfully disabled")) return;
|
||||
|
||||
if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) {
|
||||
if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module") && !/^.{1,2}$/.test(e.message)) {
|
||||
console.error("[Page Error]", e.message);
|
||||
report.otherErrors.push(e.message);
|
||||
} else {
|
||||
|
@ -299,20 +299,9 @@ page.on("pageerror", e => {
|
|||
}
|
||||
});
|
||||
|
||||
async function reporterRuntime(token: string) {
|
||||
Vencord.Webpack.waitFor(
|
||||
"loginToken",
|
||||
m => {
|
||||
console.log("[PUP_DEBUG]", "Logging in with token...");
|
||||
m.loginToken(token);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await page.evaluateOnNewDocument(`
|
||||
if (location.host.endsWith("discord.com")) {
|
||||
${readFileSync("./dist/browser.js", "utf-8")};
|
||||
(${reporterRuntime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)});
|
||||
}
|
||||
`);
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
git remote add upstream https://github.com/Vendicated/Vencord.git
|
||||
git remote set-url --pull upstream DISABLED
|
|
@ -23,10 +23,10 @@ export * as Util from "./utils";
|
|||
export * as QuickCss from "./utils/quickCss";
|
||||
export * as Updater from "./utils/updater";
|
||||
export * as Webpack from "./webpack";
|
||||
export * as WebpackPatcher from "./webpack/patchWebpack";
|
||||
export { PlainSettings, Settings };
|
||||
|
||||
import "./utils/quickCss";
|
||||
import "./webpack/patchWebpack";
|
||||
|
||||
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
|
||||
import { StartAt } from "@utils/types";
|
||||
|
@ -39,7 +39,7 @@ import { localStorage } from "./utils/localStorage";
|
|||
import { relaunch } from "./utils/native";
|
||||
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
|
||||
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
|
||||
import { onceReady } from "./webpack";
|
||||
import { onceDiscordLoaded } from "./webpack";
|
||||
import { SettingsRouter } from "./webpack/common";
|
||||
|
||||
if (IS_REPORTER) {
|
||||
|
@ -86,7 +86,7 @@ async function syncSettings() {
|
|||
}
|
||||
|
||||
async function init() {
|
||||
await onceReady;
|
||||
await onceDiscordLoaded;
|
||||
startAllPlugins(StartAt.WebpackReady);
|
||||
|
||||
syncSettings();
|
||||
|
@ -125,7 +125,7 @@ async function init() {
|
|||
const pendingPatches = patches.filter(p => !p.all && p.predicate?.() !== false);
|
||||
if (pendingPatches.length)
|
||||
PMLogger.warn(
|
||||
"Webpack has finished initialising, but some patches haven't been applied yet.",
|
||||
"Webpack has finished initializing, but some patches haven't been applied yet.",
|
||||
"This might be expected since some Modules are lazy loaded, but please verify",
|
||||
"that all plugins are working as intended.",
|
||||
"You are seeing this warning because this is a Development build of Vencord.",
|
||||
|
|
|
@ -8,13 +8,12 @@ import "./ChatButton.css";
|
|||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { waitFor } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
import { HTMLProps, MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>;
|
||||
waitFor(["buttonContainer", "channelTextArea"], m => ChannelTextAreaClasses = m);
|
||||
const ChannelTextAreaClasses = findByProps<Record<"button" | "buttonContainer", string>>("buttonContainer", "channelTextArea");
|
||||
|
||||
export interface ChatBarProps {
|
||||
channel: Channel;
|
||||
|
|
|
@ -17,14 +17,14 @@
|
|||
*/
|
||||
|
||||
import { mergeDefaults } from "@utils/mergeDefaults";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { findByCode } from "@webpack";
|
||||
import { MessageActions, SnowflakeUtils } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
import type { PartialDeep } from "type-fest";
|
||||
|
||||
import { Argument } from "./types";
|
||||
|
||||
const createBotMessage = findByCodeLazy('username:"Clyde"');
|
||||
const createBotMessage = findByCode('username:"Clyde"');
|
||||
|
||||
export function generateId() {
|
||||
return `-${SnowflakeUtils.fromTimestamp(Date.now())}`;
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { makeCodeblock } from "@utils/text";
|
||||
|
||||
import { sendBotMessage } from "./commandHelpers";
|
||||
|
@ -47,10 +46,10 @@ export let RequiredMessageOption: Option = ReqPlaceholder;
|
|||
export const _init = function (cmds: Command[]) {
|
||||
try {
|
||||
BUILT_IN = cmds;
|
||||
OptionalMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "shrug")!.options![0];
|
||||
RequiredMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "me")!.options![0];
|
||||
OptionalMessageOption = cmds.find(c => c.name === "shrug")!.options![0];
|
||||
RequiredMessageOption = cmds.find(c => c.name === "me")!.options![0];
|
||||
} catch (e) {
|
||||
new Logger("CommandsAPI").error("Failed to load CommandsApi", e, " - cmds is", cmds);
|
||||
console.error("Failed to load CommandsApi");
|
||||
}
|
||||
return cmds;
|
||||
} as never;
|
||||
|
@ -139,8 +138,6 @@ export function registerCommand<C extends Command>(command: C, plugin: string) {
|
|||
throw new Error(`Command '${command.name}' already exists.`);
|
||||
|
||||
command.isVencordCommand = true;
|
||||
command.untranslatedName ??= command.name;
|
||||
command.untranslatedDescription ??= command.description;
|
||||
command.id ??= `-${BUILT_IN.length + 1}`;
|
||||
command.applicationId ??= "-1"; // BUILT_IN;
|
||||
command.type ??= ApplicationCommandType.CHAT_INPUT;
|
||||
|
|
|
@ -93,10 +93,8 @@ export interface Command {
|
|||
isVencordCommand?: boolean;
|
||||
|
||||
name: string;
|
||||
untranslatedName?: string;
|
||||
displayName?: string;
|
||||
description: string;
|
||||
untranslatedDescription?: string;
|
||||
displayDescription?: string;
|
||||
|
||||
options?: Option[];
|
||||
|
|
|
@ -90,20 +90,19 @@ export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallba
|
|||
* A helper function for finding the children array of a group nested inside a context menu based on the id(s) of its children
|
||||
* @param id The id of the child. If an array is specified, all ids will be tried
|
||||
* @param children The context menu children
|
||||
* @param matchSubstring Whether to check if the id is a substring of the child id
|
||||
*/
|
||||
export function findGroupChildrenByChildId(id: string | string[], children: Array<ReactElement | null | undefined>, matchSubstring = false): Array<ReactElement | null | undefined> | null {
|
||||
export function findGroupChildrenByChildId(id: string | string[], children: Array<ReactElement | null>): Array<ReactElement | null> | null {
|
||||
for (const child of children) {
|
||||
if (child == null) continue;
|
||||
|
||||
if (Array.isArray(child)) {
|
||||
const found = findGroupChildrenByChildId(id, child, matchSubstring);
|
||||
const found = findGroupChildrenByChildId(id, child);
|
||||
if (found !== null) return found;
|
||||
}
|
||||
|
||||
if (
|
||||
(Array.isArray(id) && id.some(id => matchSubstring ? child.props?.id?.includes(id) : child.props?.id === id))
|
||||
|| (matchSubstring ? child.props?.id?.includes(id) : child.props?.id === id)
|
||||
(Array.isArray(id) && id.some(id => child.props?.id === id))
|
||||
|| child.props?.id === id
|
||||
) return children;
|
||||
|
||||
let nextChildren = child.props?.children;
|
||||
|
@ -113,7 +112,7 @@ export function findGroupChildrenByChildId(id: string | string[], children: Arra
|
|||
child.props.children = nextChildren;
|
||||
}
|
||||
|
||||
const found = findGroupChildrenByChildId(id, nextChildren, matchSubstring);
|
||||
const found = findGroupChildrenByChildId(id, nextChildren);
|
||||
if (found !== null) return found;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,23 +16,22 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { waitFor } from "@webpack";
|
||||
import { find } from "@webpack";
|
||||
|
||||
let NoticesModule: any;
|
||||
waitFor(m => m.show && m.dismiss && !m.suppressAll, m => NoticesModule = m);
|
||||
const Notices = find(m => m.show && m.dismiss && !m.suppressAll);
|
||||
|
||||
export const noticesQueue = [] as any[];
|
||||
export let currentNotice: any = null;
|
||||
|
||||
export function popNotice() {
|
||||
NoticesModule.dismiss();
|
||||
Notices.dismiss();
|
||||
}
|
||||
|
||||
export function nextNotice() {
|
||||
currentNotice = noticesQueue.shift();
|
||||
|
||||
if (currentNotice) {
|
||||
NoticesModule.show(...currentNotice, "VencordNotice");
|
||||
Notices.show(...currentNotice, "VencordNotice");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,9 +32,10 @@ export interface Settings {
|
|||
autoUpdate: boolean;
|
||||
autoUpdateNotification: boolean,
|
||||
useQuickCss: boolean;
|
||||
enableReactDevtools: boolean;
|
||||
themeLinks: string[];
|
||||
eagerPatches: boolean;
|
||||
enabledThemes: string[];
|
||||
enableReactDevtools: boolean;
|
||||
frameless: boolean;
|
||||
transparent: boolean;
|
||||
winCtrlQ: boolean;
|
||||
|
@ -81,6 +82,7 @@ const DefaultSettings: Settings = {
|
|||
autoUpdateNotification: true,
|
||||
useQuickCss: true,
|
||||
themeLinks: [],
|
||||
eagerPatches: IS_REPORTER,
|
||||
enabledThemes: [],
|
||||
enableReactDevtools: false,
|
||||
frameless: false,
|
||||
|
@ -116,7 +118,6 @@ const saveSettingsOnFrequentAction = debounce(async () => {
|
|||
}
|
||||
}, 60_000);
|
||||
|
||||
|
||||
export const SettingsStore = new SettingsStoreClass(settings, {
|
||||
readOnly: true,
|
||||
getDefaultValue({
|
||||
|
|
|
@ -89,8 +89,8 @@ export const isStyleEnabled = (name: string) => requireStyle(name).dom?.isConnec
|
|||
* // -- plugin.ts --
|
||||
* import pluginStyle from "./plugin.css?managed";
|
||||
* import { setStyleVars } from "@api/Styles";
|
||||
* import { findByPropsLazy } from "@webpack";
|
||||
* const classNames = findByPropsLazy("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... }
|
||||
* import { findByProps } from "@webpack";
|
||||
* const classNames = findByProps("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... }
|
||||
*
|
||||
* // Inside some plugin method like "start()"
|
||||
* setStyleClassNames(pluginStyle, classNames);
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
*/
|
||||
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { findModuleId, proxyLazyWebpack, wreq } from "@webpack";
|
||||
import { findByFactoryCode } from "@webpack";
|
||||
|
||||
interface UserSettingDefinition<T> {
|
||||
/**
|
||||
|
@ -43,12 +42,7 @@ interface UserSettingDefinition<T> {
|
|||
userSettingsAPIName: string;
|
||||
}
|
||||
|
||||
export const UserSettings: Record<PropertyKey, UserSettingDefinition<any>> | undefined = proxyLazyWebpack(() => {
|
||||
const modId = findModuleId('"textAndImages","renderSpoilers"');
|
||||
if (modId == null) return new Logger("UserSettingsAPI ").error("Didn't find settings module.");
|
||||
|
||||
return wreq(modId as any);
|
||||
});
|
||||
export const UserSettings = findByFactoryCode<Record<PropertyKey, UserSettingDefinition<any>>>('"textAndImages","renderSpoilers"');
|
||||
|
||||
/**
|
||||
* Get the setting with the given setting group and name.
|
||||
|
@ -56,7 +50,7 @@ export const UserSettings: Record<PropertyKey, UserSettingDefinition<any>> | und
|
|||
* @param group The setting group
|
||||
* @param name The name of the setting
|
||||
*/
|
||||
export function getUserSetting<T = any>(group: string, name: string): UserSettingDefinition<T> | undefined {
|
||||
export function getUserSetting<T = any>(group: string, name: string): UserSettingDefinition<T> {
|
||||
if (!Vencord.Plugins.isPluginEnabled("UserSettingsAPI")) throw new Error("Cannot use UserSettingsAPI without setting as dependency.");
|
||||
|
||||
for (const key in UserSettings) {
|
||||
|
@ -66,10 +60,12 @@ export function getUserSetting<T = any>(group: string, name: string): UserSettin
|
|||
return userSetting;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`UserSettingsAPI: Setting ${group}.${name} not found.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link getUserSettingDefinition}, lazy.
|
||||
* Lazy version of {@link getUserSetting}
|
||||
*
|
||||
* Get the setting with the given setting group and name.
|
||||
*
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Parser } from "@webpack/common";
|
||||
|
||||
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer");
|
||||
const CodeContainerClasses = findByProps("markup", "codeContainer");
|
||||
|
||||
/**
|
||||
* Renders code in a Discord codeblock
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
|
||||
import "./iconStyles.css";
|
||||
|
||||
import { getIntlMessage, getTheme, Theme } from "@utils/discord";
|
||||
import { getTheme, Theme } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import { i18n } from "@webpack/common";
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
interface BaseIconProps extends IconProps {
|
||||
|
@ -64,7 +65,8 @@ export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Discord's copy icon, as seen in the user panel popout on the right of the username and in large code blocks
|
||||
* Discord's copy icon, as seen in the user popout right of the username when clicking
|
||||
* your own username in the bottom left user panel
|
||||
*/
|
||||
export function CopyIcon(props: IconProps) {
|
||||
return (
|
||||
|
@ -74,9 +76,8 @@ export function CopyIcon(props: IconProps) {
|
|||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="currentColor">
|
||||
<path d="M3 16a1 1 0 0 1-1-1v-5a8 8 0 0 1 8-8h5a1 1 0 0 1 1 1v.5a.5.5 0 0 1-.5.5H10a6 6 0 0 0-6 6v5.5a.5.5 0 0 1-.5.5H3Z" />
|
||||
<path d="M6 18a4 4 0 0 0 4 4h8a4 4 0 0 0 4-4v-4h-3a5 5 0 0 1-5-5V6h-4a4 4 0 0 0-4 4v8Z" />
|
||||
<path d="M21.73 12a3 3 0 0 0-.6-.88l-4.25-4.24a3 3 0 0 0-.88-.61V9a3 3 0 0 0 3 3h2.73Z" />
|
||||
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1z" />
|
||||
<path d="M15 5H8c-1.1 0-1.99.9-1.99 2L6 21c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V11l-6-6zM8 21V7h6v5h5v9H8z" />
|
||||
</g>
|
||||
</Icon>
|
||||
);
|
||||
|
@ -132,7 +133,7 @@ export function InfoIcon(props: IconProps) {
|
|||
export function OwnerCrownIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
aria-label={getIntlMessage("GUILD_OWNER")}
|
||||
aria-label={i18n.Messages.GUILD_OWNER}
|
||||
{...props}
|
||||
className={classes(props.className, "vc-owner-crown-icon")}
|
||||
role="img"
|
||||
|
|
|
@ -24,13 +24,12 @@ import { classNameFactory } from "@api/Styles";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { gitRemote } from "@shared/vencordUserAgent";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { OptionType, Plugin } from "@utils/types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
||||
import { find, findByProps, findComponentByCode } from "@webpack";
|
||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserUtils } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import { Constructor } from "type-fest";
|
||||
|
||||
|
@ -50,9 +49,9 @@ import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
|||
|
||||
const cl = classNameFactory("vc-plugin-modal-");
|
||||
|
||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
|
||||
const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const AvatarStyles = findByProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||
const UserRecord = find<Constructor<Partial<User>>>(m => m?.prototype?.getAvatarURL && m?.prototype?.hasHadPremium);
|
||||
|
||||
interface PluginModalProps extends ModalProps {
|
||||
plugin: Plugin;
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { OptionType, PluginOptionNumber } from "@utils/types";
|
||||
import { Forms, React, TextInput } from "@webpack/common";
|
||||
|
||||
|
@ -56,8 +54,7 @@ export function SettingNumericComponent({ option, pluginSettings, definedSetting
|
|||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<Forms.FormTitle>{option.description}</Forms.FormTitle>
|
||||
<TextInput
|
||||
type="number"
|
||||
pattern="-?[0-9]+"
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionSelect } from "@utils/types";
|
||||
import { Forms, React, Select } from "@webpack/common";
|
||||
|
||||
|
@ -46,8 +44,7 @@ export function SettingSelectComponent({ option, pluginSettings, definedSettings
|
|||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom16} type="description">{option.description}</Forms.FormText>
|
||||
<Forms.FormTitle>{option.description}</Forms.FormTitle>
|
||||
<Select
|
||||
isDisabled={option.disabled?.call(definedSettings) ?? false}
|
||||
options={option.options}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionSlider } from "@utils/types";
|
||||
import { Forms, React, Slider } from "@webpack/common";
|
||||
|
||||
|
@ -52,8 +50,7 @@ export function SettingSliderComponent({ option, pluginSettings, definedSettings
|
|||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<Forms.FormTitle>{option.description}</Forms.FormTitle>
|
||||
<Slider
|
||||
disabled={option.disabled?.call(definedSettings) ?? false}
|
||||
markers={option.markers}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionString } from "@utils/types";
|
||||
import { Forms, React, TextInput } from "@webpack/common";
|
||||
|
||||
|
@ -43,8 +41,7 @@ export function SettingTextComponent({ option, pluginSettings, definedSettings,
|
|||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<Forms.FormTitle>{option.description}</Forms.FormTitle>
|
||||
<TextInput
|
||||
type="text"
|
||||
value={state}
|
||||
|
|
|
@ -33,19 +33,19 @@ import { Margins } from "@utils/margins";
|
|||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { Plugin } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo } from "@webpack/common";
|
||||
|
||||
import Plugins, { ExcludedPlugins } from "~plugins";
|
||||
|
||||
// Avoid circular dependency
|
||||
const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins"));
|
||||
const PluginManager = proxyLazy(() => require("../../plugins")) as typeof import("../../plugins");
|
||||
|
||||
const cl = classNameFactory("vc-plugins-");
|
||||
const logger = new Logger("PluginSettings", "#a6d189");
|
||||
|
||||
const InputStyles = findByPropsLazy("inputWrapper", "inputDefault", "error");
|
||||
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
||||
const InputStyles = findByProps("inputWrapper", "inputDefault", "error");
|
||||
const ButtonClasses = findByProps("button", "disabled", "enabled");
|
||||
|
||||
|
||||
function showErrorToast(message: string) {
|
||||
|
@ -93,14 +93,14 @@ interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
|
|||
export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
|
||||
const settings = Settings.plugins[plugin.name];
|
||||
|
||||
const isEnabled = () => Vencord.Plugins.isPluginEnabled(plugin.name);
|
||||
const isEnabled = () => settings.enabled ?? false;
|
||||
|
||||
function toggleEnabled() {
|
||||
const wasEnabled = isEnabled();
|
||||
|
||||
// If we're enabling a plugin, make sure all deps are enabled recursively.
|
||||
if (!wasEnabled) {
|
||||
const { restartNeeded, failures } = startDependenciesRecursive(plugin);
|
||||
const { restartNeeded, failures } = PluginManager.startDependenciesRecursive(plugin);
|
||||
if (failures.length) {
|
||||
logger.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`);
|
||||
showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null);
|
||||
|
@ -126,7 +126,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
|
|||
return;
|
||||
}
|
||||
|
||||
const result = wasEnabled ? stopPlugin(plugin) : startPlugin(plugin);
|
||||
const result = wasEnabled ? PluginManager.stopPlugin(plugin) : PluginManager.startPlugin(plugin);
|
||||
|
||||
if (!result) {
|
||||
settings.enabled = false;
|
||||
|
@ -292,10 +292,10 @@ export default function PluginSettings() {
|
|||
|
||||
if (!pluginFilter(p)) continue;
|
||||
|
||||
const isRequired = p.required || p.isDependency || depMap[p.name]?.some(d => settings.plugins[d].enabled);
|
||||
const isRequired = p.required || depMap[p.name]?.some(d => settings.plugins[d].enabled);
|
||||
|
||||
if (isRequired) {
|
||||
const tooltipText = p.required || !depMap[p.name]
|
||||
const tooltipText = p.required
|
||||
? "This plugin is required for Vencord to function."
|
||||
: makeDependencyList(depMap[p.name]?.filter(d => settings.plugins[d].enabled));
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import "./Switch.css";
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
|
||||
interface SwitchProps {
|
||||
checked: boolean;
|
||||
|
@ -29,7 +29,7 @@ interface SwitchProps {
|
|||
|
||||
const SWITCH_ON = "var(--green-360)";
|
||||
const SWITCH_OFF = "var(--primary-400)";
|
||||
const SwitchClasses = findByPropsLazy("slider", "input", "container");
|
||||
const SwitchClasses = findByProps("slider", "input", "container");
|
||||
|
||||
export function Switch({ checked, onChange, disabled }: SwitchProps) {
|
||||
return (
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Margins } from "@utils/margins";
|
|||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||
import { makeCodeblock } from "@utils/text";
|
||||
import { Patch, ReplaceFn } from "@utils/types";
|
||||
import { search } from "@webpack";
|
||||
import { searchFactories } from "@webpack";
|
||||
import { Button, Clipboard, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common";
|
||||
|
||||
import { SettingsTab, wrapTab } from "./shared";
|
||||
|
@ -33,7 +33,7 @@ if (IS_DEV) {
|
|||
}
|
||||
|
||||
const findCandidates = debounce(function ({ find, setModule, setError }) {
|
||||
const candidates = search(find);
|
||||
const candidates = searchFactories(find);
|
||||
const keys = Object.keys(candidates);
|
||||
const len = keys.length;
|
||||
if (len === 0)
|
||||
|
@ -56,7 +56,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
|
|||
const [compileResult, setCompileResult] = React.useState<[boolean, string]>();
|
||||
|
||||
const [patchedCode, matchResult, diff] = React.useMemo(() => {
|
||||
const src: string = fact.toString().replaceAll("\n", "");
|
||||
const src = String(fact).replaceAll("\n", "");
|
||||
|
||||
try {
|
||||
new RegExp(match);
|
||||
|
@ -247,7 +247,7 @@ function FullPatchInput({ setFind, setParsedFind, setMatch, setReplacement }: Fu
|
|||
}
|
||||
|
||||
try {
|
||||
const parsed = (0, eval)(`([${fullPatch}][0])`) as Patch;
|
||||
const parsed = (0, eval)(`(${fullPatch})`) as Patch;
|
||||
|
||||
if (!parsed.find) throw new Error("No 'find' field");
|
||||
if (!parsed.replacement) throw new Error("No 'replacement' field");
|
||||
|
@ -382,7 +382,6 @@ function PatchHelper() {
|
|||
<Forms.FormTitle className={Margins.top20}>Code</Forms.FormTitle>
|
||||
<CodeBlock lang="js" content={code} />
|
||||
<Button onClick={() => Clipboard.copy(code)}>Copy to Clipboard</Button>
|
||||
<Button className={Margins.top8} onClick={() => Clipboard.copy("```ts\n" + code + "\n```")}>Copy as Codeblock</Button>
|
||||
</>
|
||||
)}
|
||||
</SettingsTab>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings, useSettings } from "@api/Settings";
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { DeleteIcon, FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
|
||||
|
@ -27,9 +27,9 @@ import { openInviteModal } from "@utils/discord";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { showItemInFolder } from "@utils/native";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { findLazy } from "@webpack";
|
||||
import { findComponentByFields } from "@webpack";
|
||||
import { Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
|
||||
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
||||
import type { Ref, SyntheticEvent } from "react";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
|
@ -37,14 +37,14 @@ import { AddonCard } from "./AddonCard";
|
|||
import { QuickAction, QuickActionCard } from "./quickActions";
|
||||
import { SettingsTab, wrapTab } from "./shared";
|
||||
|
||||
type FileInput = ComponentType<{
|
||||
type FileInputProps = {
|
||||
ref: Ref<HTMLInputElement>;
|
||||
onChange: (e: SyntheticEvent<HTMLInputElement>) => void;
|
||||
multiple?: boolean;
|
||||
filters?: { name?: string; extensions: string[]; }[];
|
||||
}>;
|
||||
};
|
||||
|
||||
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef);
|
||||
const FileInput = findComponentByFields<FileInputProps>("activateUploadDialogue", "setRef");
|
||||
|
||||
const cl = classNameFactory("vc-settings-theme-");
|
||||
|
||||
|
@ -77,16 +77,8 @@ function Validators({ themeLinks }: { themeLinks: string[]; }) {
|
|||
<Forms.FormTitle className={Margins.top20} tag="h5">Validator</Forms.FormTitle>
|
||||
<Forms.FormText>This section will tell you whether your themes can successfully be loaded</Forms.FormText>
|
||||
<div>
|
||||
{themeLinks.map(rawLink => {
|
||||
const { label, link } = (() => {
|
||||
const match = /^@(light|dark) (.*)/.exec(rawLink);
|
||||
if (!match) return { label: rawLink, link: rawLink };
|
||||
|
||||
const [, mode, link] = match;
|
||||
return { label: `[${mode} mode only] ${link}`, link };
|
||||
})();
|
||||
|
||||
return <Card style={{
|
||||
{themeLinks.map(link => (
|
||||
<Card style={{
|
||||
padding: ".5em",
|
||||
marginBottom: ".5em",
|
||||
marginTop: ".5em"
|
||||
|
@ -94,11 +86,11 @@ function Validators({ themeLinks }: { themeLinks: string[]; }) {
|
|||
<Forms.FormTitle tag="h5" style={{
|
||||
overflowWrap: "break-word"
|
||||
}}>
|
||||
{label}
|
||||
{link}
|
||||
</Forms.FormTitle>
|
||||
<Validator link={link} />
|
||||
</Card>;
|
||||
})}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -257,7 +249,7 @@ function ThemesTab() {
|
|||
Icon={PaintbrushIcon}
|
||||
/>
|
||||
|
||||
{Settings.plugins.ClientTheme.enabled && (
|
||||
{Vencord.Plugins.isPluginEnabled("ClientTheme") && (
|
||||
<QuickAction
|
||||
text="Edit ClientTheme"
|
||||
action={() => openPluginModal(Plugins.ClientTheme)}
|
||||
|
@ -304,7 +296,6 @@ function ThemesTab() {
|
|||
<Card className="vc-settings-card vc-text-selectable">
|
||||
<Forms.FormTitle tag="h5">Paste links to css files here</Forms.FormTitle>
|
||||
<Forms.FormText>One link per line</Forms.FormText>
|
||||
<Forms.FormText>You can prefix lines with @light or @dark to toggle them based on your Discord theme</Forms.FormText>
|
||||
<Forms.FormText>Make sure to use direct links to files (raw or github.io)!</Forms.FormText>
|
||||
</Card>
|
||||
|
||||
|
|
|
@ -23,35 +23,61 @@ if (IS_DEV || IS_REPORTER) {
|
|||
var logger = new Logger("Tracer", "#FFD166");
|
||||
}
|
||||
|
||||
const noop = function () { };
|
||||
|
||||
export const beginTrace = !(IS_DEV || IS_REPORTER) ? noop :
|
||||
export const beginTrace = !(IS_DEV || IS_REPORTER) ? () => { } :
|
||||
function beginTrace(name: string, ...args: any[]) {
|
||||
if (name in traces)
|
||||
if (name in traces) {
|
||||
throw new Error(`Trace ${name} already exists!`);
|
||||
}
|
||||
|
||||
traces[name] = [performance.now(), args];
|
||||
};
|
||||
|
||||
export const finishTrace = !(IS_DEV || IS_REPORTER) ? noop : function finishTrace(name: string) {
|
||||
const end = performance.now();
|
||||
export const finishTrace = !(IS_DEV || IS_REPORTER) ? () => 0 :
|
||||
function finishTrace(name: string) {
|
||||
const end = performance.now();
|
||||
|
||||
const [start, args] = traces[name];
|
||||
delete traces[name];
|
||||
const [start, args] = traces[name];
|
||||
delete traces[name];
|
||||
|
||||
logger.debug(`${name} took ${end - start}ms`, args);
|
||||
};
|
||||
const totalTime = end - start;
|
||||
logger.debug(`${name} took ${totalTime}ms`, args);
|
||||
|
||||
return totalTime;
|
||||
};
|
||||
|
||||
type Func = (...args: any[]) => any;
|
||||
type TraceNameMapper<F extends Func> = (...args: Parameters<F>) => string;
|
||||
|
||||
const noopTracer =
|
||||
<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) => f;
|
||||
function noopTracerWithResults<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) {
|
||||
return function (this: unknown, ...args: Parameters<F>): [ReturnType<F>, number] {
|
||||
return [f.apply(this, args), 0];
|
||||
};
|
||||
}
|
||||
|
||||
function noopTracer<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) {
|
||||
return f;
|
||||
}
|
||||
|
||||
export const traceFunctionWithResults = !(IS_DEV || IS_REPORTER)
|
||||
? noopTracerWithResults
|
||||
: function traceFunctionWithResults<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): (this: unknown, ...args: Parameters<F>) => [ReturnType<F>, number] {
|
||||
return function (this: unknown, ...args: Parameters<F>) {
|
||||
const traceName = mapper?.(...args) ?? name;
|
||||
|
||||
beginTrace(traceName, ...arguments);
|
||||
try {
|
||||
return [f.apply(this, args), finishTrace(traceName)];
|
||||
} catch (e) {
|
||||
finishTrace(traceName);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const traceFunction = !(IS_DEV || IS_REPORTER)
|
||||
? noopTracer
|
||||
: function traceFunction<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): F {
|
||||
return function (this: any, ...args: Parameters<F>) {
|
||||
return function (this: unknown, ...args: Parameters<F>) {
|
||||
const traceName = mapper?.(...args) ?? name;
|
||||
|
||||
beginTrace(traceName, ...arguments);
|
||||
|
|
|
@ -8,10 +8,11 @@ import { Logger } from "@utils/Logger";
|
|||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import * as Webpack from "@webpack";
|
||||
import { wreq } from "@webpack";
|
||||
|
||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
||||
import { AnyModuleFactory, ModuleFactory } from "webpack";
|
||||
|
||||
export async function loadLazyChunks() {
|
||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
||||
|
||||
try {
|
||||
LazyChunkLoaderLogger.log("Loading all chunks...");
|
||||
|
||||
|
@ -25,19 +26,12 @@ export async function loadLazyChunks() {
|
|||
// True if resolved, false otherwise
|
||||
const chunksSearchPromises = [] as Array<() => boolean>;
|
||||
|
||||
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g);
|
||||
|
||||
const foundCssDebuggingLoad = false;
|
||||
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i(?:\.\i)?\.bind\(\i,"?([^)]+?)"?(?:,[^)]+?)?\)\)/g);
|
||||
|
||||
async function searchAndLoadLazyChunks(factoryCode: string) {
|
||||
// Workaround to avoid loading the CSS debugging chunk which turns the app pink
|
||||
const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : factoryCode.includes(".cssDebuggingEnabled&&");
|
||||
|
||||
const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
|
||||
const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>();
|
||||
|
||||
const shouldForceDefer = false;
|
||||
|
||||
await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => {
|
||||
const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => Number(m[1])) : [];
|
||||
|
||||
|
@ -48,16 +42,6 @@ export async function loadLazyChunks() {
|
|||
let invalidChunkGroup = false;
|
||||
|
||||
for (const id of chunkIds) {
|
||||
if (hasCssDebuggingLoad) {
|
||||
if (chunkIds.length > 1) {
|
||||
throw new Error("Found multiple chunks in factory that loads the CSS debugging chunk");
|
||||
}
|
||||
|
||||
invalidChunks.add(id);
|
||||
invalidChunkGroup = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue;
|
||||
|
||||
const isWorkerAsset = await fetch(wreq.p + wreq.u(id))
|
||||
|
@ -82,19 +66,14 @@ export async function loadLazyChunks() {
|
|||
await Promise.all(
|
||||
Array.from(validChunkGroups)
|
||||
.map(([chunkIds]) =>
|
||||
Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { })))
|
||||
Promise.all(chunkIds.map(id => wreq.e(id)))
|
||||
)
|
||||
);
|
||||
|
||||
// Requires the entry points for all valid chunk groups
|
||||
for (const [, entryPoint] of validChunkGroups) {
|
||||
try {
|
||||
if (shouldForceDefer) {
|
||||
deferredRequires.add(entryPoint);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wreq.m[entryPoint]) wreq(entryPoint as any);
|
||||
if (wreq.m[entryPoint]) wreq(entryPoint);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
@ -122,32 +101,33 @@ export async function loadLazyChunks() {
|
|||
}, 0);
|
||||
}
|
||||
|
||||
Webpack.factoryListeners.add(factory => {
|
||||
function factoryListener(factory: AnyModuleFactory | ModuleFactory) {
|
||||
let isResolved = false;
|
||||
searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true);
|
||||
|
||||
chunksSearchPromises.push(() => isResolved);
|
||||
});
|
||||
|
||||
for (const factoryId in wreq.m) {
|
||||
let isResolved = false;
|
||||
searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true);
|
||||
searchAndLoadLazyChunks(String(factory))
|
||||
.then(() => isResolved = true)
|
||||
.catch(() => isResolved = true);
|
||||
|
||||
chunksSearchPromises.push(() => isResolved);
|
||||
}
|
||||
|
||||
Webpack.factoryListeners.add(factoryListener);
|
||||
for (const factoryId in wreq.m) {
|
||||
factoryListener(wreq.m[factoryId]);
|
||||
}
|
||||
|
||||
await chunksSearchingDone;
|
||||
Webpack.factoryListeners.delete(factoryListener);
|
||||
|
||||
// Require deferred entry points
|
||||
for (const deferredRequire of deferredRequires) {
|
||||
wreq!(deferredRequire as any);
|
||||
wreq(deferredRequire);
|
||||
}
|
||||
|
||||
// All chunks Discord has mapped to asset files, even if they are not used anymore
|
||||
const allChunks = [] as number[];
|
||||
|
||||
// Matches "id" or id:
|
||||
for (const currentMatch of wreq!.u.toString().matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) {
|
||||
for (const currentMatch of String(wreq.u).matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) {
|
||||
const id = currentMatch[1] ?? currentMatch[2];
|
||||
if (id == null) continue;
|
||||
|
||||
|
@ -156,7 +136,8 @@ export async function loadLazyChunks() {
|
|||
|
||||
if (allChunks.length === 0) throw new Error("Failed to get all chunks");
|
||||
|
||||
// Chunks that are not loaded (not used) by Discord code anymore
|
||||
// Chunks which our regex could not catch to load
|
||||
// It will always contain WebWorker assets, and also currently contains some language packs which are loaded differently
|
||||
const chunksLeft = allChunks.filter(id => {
|
||||
return !(validChunks.has(id) || invalidChunks.has(id));
|
||||
});
|
||||
|
@ -166,12 +147,9 @@ export async function loadLazyChunks() {
|
|||
.then(r => r.text())
|
||||
.then(t => t.includes("importScripts("));
|
||||
|
||||
// Loads and requires a chunk
|
||||
// Loads the chunk. Currently this only happens with the language packs which are loaded differently
|
||||
if (!isWorkerAsset) {
|
||||
await wreq.e(id as any);
|
||||
// Technically, the id of the chunk does not match the entry point
|
||||
// But, still try it because we have no way to get the actual entry point
|
||||
if (wreq.m[id]) wreq(id as any);
|
||||
await wreq.e(id);
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
@ -4,22 +4,39 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
|
||||
import * as Webpack from "@webpack";
|
||||
import { patches } from "plugins";
|
||||
import { addPatch, patches } from "plugins";
|
||||
|
||||
import { loadLazyChunks } from "./loadLazyChunks";
|
||||
|
||||
const ReporterLogger = new Logger("Reporter");
|
||||
|
||||
async function runReporter() {
|
||||
const ReporterLogger = new Logger("Reporter");
|
||||
|
||||
try {
|
||||
ReporterLogger.log("Starting test...");
|
||||
|
||||
let loadLazyChunksResolve: (value: void | PromiseLike<void>) => void;
|
||||
let loadLazyChunksResolve: (value: void) => void;
|
||||
const loadLazyChunksDone = new Promise<void>(r => loadLazyChunksResolve = r);
|
||||
|
||||
Webpack.beforeInitListeners.add(() => loadLazyChunks().then((loadLazyChunksResolve)));
|
||||
// The main patch for starting the reporter chunk loading
|
||||
addPatch({
|
||||
find: '"Could not find app-mount"',
|
||||
replacement: {
|
||||
match: /(?<="use strict";)/,
|
||||
replace: "Vencord.Webpack._initReporter();"
|
||||
}
|
||||
}, "Vencord Reporter");
|
||||
|
||||
// @ts-ignore
|
||||
Vencord.Webpack._initReporter = function () {
|
||||
// initReporter is called in the patched entry point of Discord
|
||||
// setImmediate to only start searching for lazy chunks after Discord initialized the app
|
||||
setTimeout(() => loadLazyChunks().then(loadLazyChunksResolve), 0);
|
||||
};
|
||||
|
||||
await loadLazyChunksDone;
|
||||
|
||||
for (const patch of patches) {
|
||||
|
@ -28,52 +45,158 @@ async function runReporter() {
|
|||
}
|
||||
}
|
||||
|
||||
for (const [searchType, args] of Webpack.lazyWebpackSearchHistory) {
|
||||
let method = searchType;
|
||||
|
||||
if (searchType === "findComponent") method = "find";
|
||||
if (searchType === "findExportedComponent") method = "findByProps";
|
||||
if (searchType === "waitFor" || searchType === "waitForComponent") {
|
||||
if (typeof args[0] === "string") method = "findByProps";
|
||||
else method = "find";
|
||||
for (const [plugin, moduleId, match, totalTime] of Vencord.WebpackPatcher.patchTimings) {
|
||||
if (totalTime > 3) {
|
||||
new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${totalTime}ms (Module id is ${String(moduleId)}): ${match}`);
|
||||
}
|
||||
if (searchType === "waitForStore") method = "findStore";
|
||||
}
|
||||
|
||||
let result: any;
|
||||
await Promise.all(Webpack.webpackSearchHistory.map(async ([searchType, args]) => {
|
||||
args = [...args];
|
||||
|
||||
let result = null as any;
|
||||
try {
|
||||
if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") {
|
||||
const [factory] = args;
|
||||
result = factory();
|
||||
} else if (method === "extractAndLoadChunks") {
|
||||
const [code, matcher] = args;
|
||||
switch (searchType) {
|
||||
case "webpackDependantLazy":
|
||||
case "webpackDependantLazyComponent": {
|
||||
const [factory] = args;
|
||||
result = factory();
|
||||
break;
|
||||
}
|
||||
case "extractAndLoadChunks": {
|
||||
const extractAndLoadChunks = args.shift();
|
||||
|
||||
result = await Webpack.extractAndLoadChunks(code, matcher);
|
||||
if (result === false) result = null;
|
||||
} else if (method === "mapMangledModule") {
|
||||
const [code, mapper] = args;
|
||||
result = await extractAndLoadChunks();
|
||||
if (result === false) {
|
||||
result = null;
|
||||
}
|
||||
|
||||
result = Webpack.mapMangledModule(code, mapper);
|
||||
if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail");
|
||||
} else {
|
||||
// @ts-ignore
|
||||
result = Webpack[method](...args);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const findResult = args.shift();
|
||||
|
||||
if (findResult != null) {
|
||||
if (findResult.$$vencordCallbackCalled != null && findResult.$$vencordCallbackCalled()) {
|
||||
result = findResult;
|
||||
break;
|
||||
}
|
||||
|
||||
if (findResult[SYM_PROXY_INNER_GET] != null) {
|
||||
result = findResult[SYM_PROXY_INNER_VALUE];
|
||||
break;
|
||||
}
|
||||
|
||||
if (findResult[SYM_LAZY_COMPONENT_INNER] != null) {
|
||||
result = findResult[SYM_LAZY_COMPONENT_INNER]();
|
||||
break;
|
||||
}
|
||||
|
||||
if (searchType === "mapMangledModule") {
|
||||
result = findResult;
|
||||
|
||||
for (const innerMap in result) {
|
||||
if (
|
||||
(result[innerMap][SYM_PROXY_INNER_GET] != null && result[innerMap][SYM_PROXY_INNER_VALUE] == null) ||
|
||||
(result[innerMap][SYM_LAZY_COMPONENT_INNER] != null && result[innerMap][SYM_LAZY_COMPONENT_INNER]() == null)
|
||||
) {
|
||||
throw new Error("Webpack Find Fail");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// This can happen if a `find` was immediately found
|
||||
result = findResult;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail");
|
||||
if (result == null) {
|
||||
throw new Error("Webpack Find Fail");
|
||||
}
|
||||
} catch (e) {
|
||||
let logMessage = searchType;
|
||||
if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`;
|
||||
else if (method === "extractAndLoadChunks") logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`;
|
||||
else if (method === "mapMangledModule") {
|
||||
const failedMappings = Object.keys(args[1]).filter(key => result?.[key] == null);
|
||||
|
||||
logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`;
|
||||
let filterName = "";
|
||||
let parsedArgs = args;
|
||||
|
||||
if (args[0].$$vencordProps != null) {
|
||||
if (["find", "findComponent", "waitFor"].includes(searchType)) {
|
||||
filterName = args[0].$$vencordProps[0];
|
||||
parsedArgs = args[0].$$vencordProps.slice(1);
|
||||
} else {
|
||||
parsedArgs = args[0].$$vencordProps;
|
||||
}
|
||||
}
|
||||
|
||||
function stringifyFilter(code: Webpack.CodeFilterWithSingle) {
|
||||
if (Array.isArray(code)) {
|
||||
return `[${code.map(arg => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")}]`;
|
||||
}
|
||||
|
||||
return code instanceof RegExp ? String(code) : JSON.stringify(code);
|
||||
}
|
||||
|
||||
// if parsedArgs is the same as args, it means vencordProps of the filter was not available (like in normal filter functions),
|
||||
// so log the filter function instead
|
||||
if (
|
||||
parsedArgs === args &&
|
||||
["waitFor", "find", "findComponent", "webpackDependantLazy", "webpackDependantLazyComponent"].includes(searchType)
|
||||
) {
|
||||
let filter = String(parsedArgs[0]);
|
||||
if (filter.length > 150) {
|
||||
filter = filter.slice(0, 147) + "...";
|
||||
}
|
||||
|
||||
logMessage += `(${filter})`;
|
||||
} else if (searchType === "extractAndLoadChunks") {
|
||||
const [code, matcher] = parsedArgs;
|
||||
|
||||
let regexStr: string;
|
||||
if (matcher === Webpack.DefaultExtractAndLoadChunksRegex) {
|
||||
regexStr = "DefaultExtractAndLoadChunksRegex";
|
||||
} else {
|
||||
regexStr = String(matcher);
|
||||
}
|
||||
|
||||
logMessage += `(${stringifyFilter(code)}, ${regexStr})`;
|
||||
} else if (searchType === "mapMangledModule") {
|
||||
const [code, mappers] = parsedArgs;
|
||||
|
||||
const parsedFailedMappers = Object.entries<any>(mappers)
|
||||
.filter(([key]) =>
|
||||
result == null ||
|
||||
(result[key]?.[SYM_PROXY_INNER_GET] != null && result[key][SYM_PROXY_INNER_VALUE] == null) ||
|
||||
(result[key]?.[SYM_LAZY_COMPONENT_INNER] != null && result[key][SYM_LAZY_COMPONENT_INNER]() == null)
|
||||
)
|
||||
.map(([key, filter]) => {
|
||||
let parsedFilter: string;
|
||||
|
||||
if (filter.$$vencordProps != null) {
|
||||
const filterName = filter.$$vencordProps[0];
|
||||
parsedFilter = `${filterName}(${filter.$$vencordProps.slice(1).map((arg: any) => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")})`;
|
||||
} else {
|
||||
parsedFilter = String(filter);
|
||||
if (parsedFilter.length > 150) {
|
||||
parsedFilter = parsedFilter.slice(0, 147) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
return [key, parsedFilter];
|
||||
});
|
||||
|
||||
logMessage += `(${stringifyFilter(code)}, {\n${parsedFailedMappers.map(([key, parsedFilter]) => `\t${key}: ${parsedFilter}`).join(",\n")}\n})`;
|
||||
} else {
|
||||
logMessage += `(${filterName.length ? `${filterName}(` : ""}${parsedArgs.map(stringifyFilter).join(", ")})${filterName.length ? ")" : ""}`;
|
||||
}
|
||||
else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`;
|
||||
|
||||
ReporterLogger.log("Webpack Find Fail:", logMessage);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
ReporterLogger.log("Finished test");
|
||||
} catch (e) {
|
||||
|
@ -81,4 +204,5 @@ async function runReporter() {
|
|||
}
|
||||
}
|
||||
|
||||
runReporter();
|
||||
// Run after the Vencord object has been created
|
||||
setTimeout(runReporter, 0);
|
||||
|
|
7
src/globals.d.ts
vendored
7
src/globals.d.ts
vendored
|
@ -64,13 +64,8 @@ declare global {
|
|||
export var Vesktop: any;
|
||||
export var VesktopNative: any;
|
||||
|
||||
interface Window {
|
||||
webpackChunkdiscord_app: {
|
||||
push(chunk: any): any;
|
||||
pop(): any;
|
||||
};
|
||||
interface Window extends Record<PropertyKey, any> {
|
||||
_: LoDashStatic;
|
||||
[k: string]: any;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import { onceDefined } from "@shared/onceDefined";
|
||||
import electron, { app, BrowserWindowConstructorOptions, Menu, nativeTheme } from "electron";
|
||||
import electron, { app, BrowserWindowConstructorOptions, Menu } from "electron";
|
||||
import { dirname, join } from "path";
|
||||
|
||||
import { initIpc } from "./ipcMain";
|
||||
|
@ -100,19 +100,6 @@ if (!IS_VANILLA) {
|
|||
|
||||
super(options);
|
||||
initIpc(this);
|
||||
|
||||
// Workaround for https://github.com/electron/electron/issues/43367. Vesktop also has its own workaround
|
||||
// @TODO: Remove this when the issue is fixed
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
this.webContents.on("devtools-opened", () => {
|
||||
if (!nativeTheme.shouldUseDarkColors) return;
|
||||
|
||||
nativeTheme.themeSource = "light";
|
||||
setTimeout(() => {
|
||||
nativeTheme.themeSource = "dark";
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
} else super(options);
|
||||
}
|
||||
}
|
||||
|
|
3
src/plugins/_api/badges/fixBadgeOverflow.css
Normal file
3
src/plugins/_api/badges/fixBadgeOverflow.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
[class*="profileBadges"] {
|
||||
flex: none;
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./fixBadgeOverflow.css";
|
||||
|
||||
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
|
||||
import DonateButton from "@components/DonateButton";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
|
@ -77,7 +79,7 @@ export default definePlugin({
|
|||
replace: "...$1.props,$& $1.image??"
|
||||
},
|
||||
{
|
||||
match: /(?<=text:(\i)\.description,.{0,200})children:/,
|
||||
match: /(?<=text:(\i)\.description,.{0,50})children:/,
|
||||
replace: "children:$1.component ? $self.renderBadgeComponent({ ...$1 }) :"
|
||||
},
|
||||
// conditionally override their onClick with badge.onClick if it exists
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
|
||||
export default definePlugin({
|
||||
name: "DynamicImageModalAPI",
|
||||
authors: [Devs.sadan, Devs.Nuckyz],
|
||||
description: "Allows you to omit either width or height when opening an image modal",
|
||||
patches: [
|
||||
{
|
||||
find: "SCALE_DOWN:",
|
||||
replacement: {
|
||||
match: /!\(null==(\i)\|\|0===\i\|\|null==(\i)\|\|0===\i\)/,
|
||||
replace: (_, width, height) => `!((null == ${width} || 0 === ${width}) && (null == ${height} || 0 === ${height}))`
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
|
@ -31,7 +31,7 @@ export default definePlugin({
|
|||
match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/,
|
||||
replace: "$&vencordProps=$1,"
|
||||
}, {
|
||||
match: /#{intl::GUILD_OWNER}(?=.+?decorators:(\i)\(\)).+?\1=?\(\)=>.+?children:\[/,
|
||||
match: /\.Messages\.GUILD_OWNER(?=.+?decorators:(\i)\(\)).+?\1=?\(\)=>.+?children:\[/,
|
||||
replace: "$&...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)),"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -25,7 +25,7 @@ export default definePlugin({
|
|||
authors: [Devs.Cyn],
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::REMOVE_ATTACHMENT_BODY}",
|
||||
find: ".Messages.REMOVE_ATTACHMENT_BODY",
|
||||
replacement: {
|
||||
match: /(?<=.container\)?,children:)(\[.+?\])/,
|
||||
replace: "Vencord.Api.MessageAccessories._modifyAccessories($1,this.props)",
|
||||
|
|
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||
{
|
||||
find: '"Message Username"',
|
||||
replacement: {
|
||||
match: /#{intl::GUILD_COMMUNICATION_DISABLED_BOTTOM_SHEET_TITLE}.+?}\),\i(?=\])/,
|
||||
match: /\.Messages\.GUILD_COMMUNICATION_DISABLED_BOTTOM_SHEET_TITLE.+?}\),\i(?=\])/,
|
||||
replace: "$&,...Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0])"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ export default definePlugin({
|
|||
authors: [Devs.Arjix, Devs.hunt, Devs.Ven],
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::EDIT_TEXTAREA_HELP}",
|
||||
find: ".Messages.EDIT_TEXTAREA_HELP",
|
||||
replacement: {
|
||||
match: /(?<=,channel:\i\}\)\.then\().+?(?=return \i\.content!==this\.props\.message\.content&&\i\((.+?)\))/,
|
||||
replace: (match, args) => "" +
|
||||
|
|
|
@ -24,9 +24,9 @@ export default definePlugin({
|
|||
description: "API to add buttons to message popovers.",
|
||||
authors: [Devs.KingFish, Devs.Ven, Devs.Nuckyz],
|
||||
patches: [{
|
||||
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
|
||||
find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
|
||||
replacement: {
|
||||
match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.string\(\i\.\i#{intl::MESSAGE_ACTION_REPLY}.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/,
|
||||
match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.Messages\.MESSAGE_ACTION_REPLY.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/,
|
||||
replace: "$&,Vencord.Api.MessagePopover._buildPopoverElements($1,$2)"
|
||||
}
|
||||
}],
|
||||
|
|
|
@ -25,16 +25,16 @@ export default definePlugin({
|
|||
description: "Api required for plugins that modify the server list",
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::DISCODO_DISABLED}",
|
||||
find: "Messages.DISCODO_DISABLED",
|
||||
replacement: {
|
||||
match: /(?<=#{intl::DISCODO_DISABLED}.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
||||
match: /(?<=Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
||||
replace: "[$1].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "#{intl::SERVERS}),children",
|
||||
find: "Messages.SERVERS,children",
|
||||
replacement: {
|
||||
match: /(?<=#{intl::SERVERS}\),children:)\i\.map\(\i\)/,
|
||||
match: /(?<=Messages\.SERVERS,children:).+?default:return null\}\}\)/,
|
||||
replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
import { WebpackRequire } from "webpack";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
disableAnalytics: {
|
||||
|
@ -48,7 +49,7 @@ export default definePlugin({
|
|||
},
|
||||
},
|
||||
{
|
||||
find: ".METRICS",
|
||||
find: ".METRICS,",
|
||||
replacement: [
|
||||
{
|
||||
match: /this\._intervalId=/,
|
||||
|
@ -59,7 +60,15 @@ export default definePlugin({
|
|||
replace: "$&return;"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
find: ".installedLogHooks)",
|
||||
replacement: {
|
||||
// if getDebugLogging() returns false, the hooks don't get installed.
|
||||
match: "getDebugLogging(){",
|
||||
replace: "getDebugLogging(){return false;"
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
startAt: StartAt.Init,
|
||||
|
@ -73,9 +82,9 @@ export default definePlugin({
|
|||
Object.defineProperty(Function.prototype, "g", {
|
||||
configurable: true,
|
||||
|
||||
set(v: any) {
|
||||
set(this: WebpackRequire, globalObj: WebpackRequire["g"]) {
|
||||
Object.defineProperty(this, "g", {
|
||||
value: v,
|
||||
value: globalObj,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
|
@ -84,11 +93,11 @@ export default definePlugin({
|
|||
// Ensure this is most likely the Sentry WebpackInstance.
|
||||
// Function.g is a very generic property and is not uncommon for another WebpackInstance (or even a React component: <g></g>) to include it
|
||||
const { stack } = new Error();
|
||||
if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || !String(this).includes("exports:{}") || this.c != null) {
|
||||
if (this.c != null || !stack?.includes("http") || !String(this).includes("exports:{}")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const assetPath = stack?.match(/\/assets\/.+?\.js/)?.[0];
|
||||
const assetPath = stack.match(/http.+?(?=:\d+?:\d+?$)/m)?.[0];
|
||||
if (!assetPath) {
|
||||
return;
|
||||
}
|
||||
|
@ -98,7 +107,8 @@ export default definePlugin({
|
|||
srcRequest.send();
|
||||
|
||||
// Final condition to see if this is the Sentry WebpackInstance
|
||||
if (!srcRequest.responseText.includes("window.DiscordSentry=")) {
|
||||
// This is matching window.DiscordSentry=, but without `window` to avoid issues on some proxies
|
||||
if (!srcRequest.responseText.includes(".DiscordSentry=")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import BackupAndRestoreTab from "@components/VencordSettings/BackupAndRestoreTab";
|
||||
import CloudTab from "@components/VencordSettings/CloudTab";
|
||||
import PatchHelperTab from "@components/VencordSettings/PatchHelperTab";
|
||||
|
@ -25,20 +25,35 @@ import ThemesTab from "@components/VencordSettings/ThemesTab";
|
|||
import UpdaterTab from "@components/VencordSettings/UpdaterTab";
|
||||
import VencordTab from "@components/VencordSettings/VencordTab";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { React } from "@webpack/common";
|
||||
import { i18n, React } from "@webpack/common";
|
||||
|
||||
import gitHash from "~git-hash";
|
||||
|
||||
type SectionType = "HEADER" | "DIVIDER" | "CUSTOM";
|
||||
type SectionTypes = Record<SectionType, SectionType>;
|
||||
|
||||
const settings = definePluginSettings({
|
||||
settingsLocation: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Where to put the Vencord settings section",
|
||||
options: [
|
||||
{ label: "At the very top", value: "top" },
|
||||
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
|
||||
{ label: "Below the Nitro section", value: "belowNitro" },
|
||||
{ label: "Above Activity Settings", value: "aboveActivity" },
|
||||
{ label: "Below Activity Settings", value: "belowActivity" },
|
||||
{ label: "At the very bottom", value: "bottom" },
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "Settings",
|
||||
description: "Adds Settings UI and debug info",
|
||||
authors: [Devs.Ven, Devs.Megu],
|
||||
required: true,
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
|
@ -58,20 +73,20 @@ export default definePlugin({
|
|||
]
|
||||
},
|
||||
{
|
||||
find: ".SEARCH_NO_RESULTS&&0===",
|
||||
find: "Messages.ACTIVITY_SETTINGS",
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/,
|
||||
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
|
||||
},
|
||||
{
|
||||
match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
|
||||
match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,30}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
|
||||
replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})`
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
||||
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL",
|
||||
replacement: {
|
||||
match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/,
|
||||
replace: "$2.open($1);return;"
|
||||
|
@ -137,30 +152,25 @@ export default definePlugin({
|
|||
].filter(Boolean);
|
||||
},
|
||||
|
||||
isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) {
|
||||
const firstChild = settings?.[0];
|
||||
isRightSpot({ header, settingsChilds }: { header?: string; settingsChilds?: string[]; }) {
|
||||
const firstChild = settingsChilds?.[0];
|
||||
// lowest two elements... sanity backup
|
||||
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true;
|
||||
|
||||
const { settingsLocation } = Settings.plugins.Settings;
|
||||
const { settingsLocation } = settings.store;
|
||||
|
||||
if (settingsLocation === "bottom") return firstChild === "LOGOUT";
|
||||
if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG";
|
||||
|
||||
if (!header) return;
|
||||
|
||||
try {
|
||||
const names = {
|
||||
top: getIntlMessage("USER_SETTINGS"),
|
||||
aboveNitro: getIntlMessage("BILLING_SETTINGS"),
|
||||
belowNitro: getIntlMessage("APP_SETTINGS"),
|
||||
aboveActivity: getIntlMessage("ACTIVITY_SETTINGS")
|
||||
};
|
||||
|
||||
return header === names[settingsLocation];
|
||||
} catch {
|
||||
return firstChild === "PREMIUM";
|
||||
}
|
||||
const names = {
|
||||
top: i18n.Messages.USER_SETTINGS,
|
||||
aboveNitro: i18n.Messages.BILLING_SETTINGS,
|
||||
belowNitro: i18n.Messages.APP_SETTINGS,
|
||||
aboveActivity: i18n.Messages.ACTIVITY_SETTINGS
|
||||
};
|
||||
return header === names[settingsLocation];
|
||||
},
|
||||
|
||||
patchedSettings: new WeakSet(),
|
||||
|
@ -187,23 +197,8 @@ export default definePlugin({
|
|||
};
|
||||
},
|
||||
|
||||
options: {
|
||||
settingsLocation: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Where to put the Vencord settings section",
|
||||
options: [
|
||||
{ label: "At the very top", value: "top" },
|
||||
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
|
||||
{ label: "Below the Nitro section", value: "belowNitro" },
|
||||
{ label: "Above Activity Settings", value: "aboveActivity" },
|
||||
{ label: "Below Activity Settings", value: "belowActivity" },
|
||||
{ label: "At the very bottom", value: "bottom" },
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
get electronVersion() {
|
||||
return VencordNative.native.getVersions().electron || window.legcord?.electron || null;
|
||||
return VencordNative.native.getVersions().electron || window.armcord?.electron || null;
|
||||
},
|
||||
|
||||
get chromiumVersion() {
|
||||
|
|
|
@ -59,7 +59,7 @@ const TrustedRolesIds = [
|
|||
|
||||
const AsyncFunction = async function () { }.constructor;
|
||||
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
|
||||
|
||||
async function forceUpdate() {
|
||||
const outdated = await checkForUpdates();
|
||||
|
@ -77,7 +77,7 @@ async function generateDebugInfoMessage() {
|
|||
const client = (() => {
|
||||
if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`;
|
||||
if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`;
|
||||
if ("legcord" in window) return `Legcord v${window.legcord.version}`;
|
||||
if ("armcord" in window) return `ArmCord v${window.armcord.version}`;
|
||||
|
||||
// @ts-expect-error
|
||||
const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web";
|
||||
|
@ -142,15 +142,15 @@ export default definePlugin({
|
|||
required: true,
|
||||
description: "Helps us provide support to you",
|
||||
authors: [Devs.Ven],
|
||||
dependencies: ["UserSettingsAPI", "MessageAccessoriesAPI"],
|
||||
dependencies: ["CommandsAPI", "UserSettingsAPI", "MessageAccessoriesAPI"],
|
||||
|
||||
settings,
|
||||
|
||||
patches: [{
|
||||
find: "#{intl::BEGINNING_DM}",
|
||||
find: ".BEGINNING_DM.format",
|
||||
replacement: {
|
||||
match: /#{intl::BEGINNING_DM},{.+?}\),(?=.{0,300}(\i)\.isMultiUserDM)/,
|
||||
replace: "$& $self.renderContributorDmWarningCard({ channel: $1 }),"
|
||||
match: /BEGINNING_DM\.format\(\{.+?\}\),(?=.{0,100}userId:(\i\.getRecipientId\(\)))/,
|
||||
replace: "$& $self.ContributorDmWarningCard({ userId: $1 }),"
|
||||
}
|
||||
}],
|
||||
|
||||
|
@ -235,8 +235,7 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
|
||||
renderContributorDmWarningCard: ErrorBoundary.wrap(({ channel }) => {
|
||||
const userId = channel.getRecipientId();
|
||||
ContributorDmWarningCard: ErrorBoundary.wrap(({ userId }) => {
|
||||
if (!isPluginDev(userId)) return null;
|
||||
if (RelationshipStore.isFriend(userId) || isPluginDev(UserStore.getCurrentUser()?.id)) return null;
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
# AccountPanelServerProfile
|
||||
|
||||
Right click your account panel in the bottom left to view your profile in the current server
|
||||
|
||||
![](https://github.com/user-attachments/assets/3228497d-488f-479c-93d2-a32ccdb08f0f)
|
||||
|
||||
![](https://github.com/user-attachments/assets/6fc45363-d95f-4810-812f-2f9fb28b41b5)
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentChannel } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
interface UserProfileProps {
|
||||
popoutProps: Record<string, any>;
|
||||
currentUser: User;
|
||||
originalPopout: () => React.ReactNode;
|
||||
}
|
||||
|
||||
const UserProfile = findComponentByCodeLazy("UserProfilePopoutWrapper: user cannot be undefined");
|
||||
const styles = findByPropsLazy("accountProfilePopoutWrapper");
|
||||
|
||||
let openAlternatePopout = false;
|
||||
let accountPanelRef: React.MutableRefObject<Record<PropertyKey, any> | null> = { current: null };
|
||||
|
||||
const AccountPanelContextMenu = ErrorBoundary.wrap(() => {
|
||||
const { prioritizeServerProfile } = settings.use(["prioritizeServerProfile"]);
|
||||
|
||||
return (
|
||||
<Menu.Menu
|
||||
navId="vc-ap-server-profile"
|
||||
onClose={ContextMenuApi.closeContextMenu}
|
||||
>
|
||||
<Menu.MenuItem
|
||||
id="vc-ap-view-alternate-popout"
|
||||
label={prioritizeServerProfile ? "View Account Profile" : "View Server Profile"}
|
||||
disabled={getCurrentChannel()?.getGuildId() == null}
|
||||
action={e => {
|
||||
openAlternatePopout = true;
|
||||
accountPanelRef.current?.props.onMouseDown();
|
||||
accountPanelRef.current?.props.onClick(e);
|
||||
}}
|
||||
/>
|
||||
<Menu.MenuCheckboxItem
|
||||
id="vc-ap-prioritize-server-profile"
|
||||
label="Prioritize Server Profile"
|
||||
checked={prioritizeServerProfile}
|
||||
action={() => settings.store.prioritizeServerProfile = !prioritizeServerProfile}
|
||||
/>
|
||||
</Menu.Menu>
|
||||
);
|
||||
}, { noop: true });
|
||||
|
||||
const settings = definePluginSettings({
|
||||
prioritizeServerProfile: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Prioritize Server Profile when left clicking your account panel",
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "AccountPanelServerProfile",
|
||||
description: "Right click your account panel in the bottom left to view your profile in the current server",
|
||||
authors: [Devs.Nuckyz, Devs.relitrix],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=\.SIZE_32\)}\);)/,
|
||||
replace: "$self.useAccountPanelRef();"
|
||||
},
|
||||
{
|
||||
match: /(\.AVATAR,children:.+?renderPopout:(\i)=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/,
|
||||
replace: (_, rest, popoutProps, originalPopout, currentUser) => `${rest}$self.UserProfile({popoutProps:${popoutProps},currentUser:${currentUser},originalPopout:()=>{${originalPopout}}})`
|
||||
},
|
||||
{
|
||||
match: /\.AVATAR,children:.+?(?=renderPopout:)/,
|
||||
replace: "$&onRequestClose:$self.onPopoutClose,"
|
||||
},
|
||||
{
|
||||
match: /(?<=.avatarWrapper,)/,
|
||||
replace: "ref:$self.accountPanelRef,onContextMenu:$self.openAccountPanelContextMenu,"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
get accountPanelRef() {
|
||||
return accountPanelRef;
|
||||
},
|
||||
|
||||
useAccountPanelRef() {
|
||||
useEffect(() => () => {
|
||||
accountPanelRef.current = null;
|
||||
}, []);
|
||||
|
||||
return (accountPanelRef = useRef(null));
|
||||
},
|
||||
|
||||
openAccountPanelContextMenu(event: React.UIEvent) {
|
||||
ContextMenuApi.openContextMenu(event, AccountPanelContextMenu);
|
||||
},
|
||||
|
||||
onPopoutClose() {
|
||||
openAlternatePopout = false;
|
||||
},
|
||||
|
||||
UserProfile: ErrorBoundary.wrap(({ popoutProps, currentUser, originalPopout }: UserProfileProps) => {
|
||||
if (
|
||||
(settings.store.prioritizeServerProfile && openAlternatePopout) ||
|
||||
(!settings.store.prioritizeServerProfile && !openAlternatePopout)
|
||||
) {
|
||||
return originalPopout();
|
||||
}
|
||||
|
||||
const currentChannel = getCurrentChannel();
|
||||
if (currentChannel?.getGuildId() == null) {
|
||||
return originalPopout();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.accountProfilePopoutWrapper}>
|
||||
<UserProfile {...popoutProps} userId={currentUser.id} guildId={currentChannel.getGuildId()} channelId={currentChannel.id} />
|
||||
</div>
|
||||
);
|
||||
}, { noop: true })
|
||||
});
|
|
@ -41,7 +41,7 @@ export default definePlugin({
|
|||
},
|
||||
{
|
||||
// Status emojis
|
||||
find: "#{intl::GUILD_OWNER}",
|
||||
find: ".Messages.GUILD_OWNER,",
|
||||
replacement: {
|
||||
match: /(?<=\.activityEmoji,.+?animate:)\i/,
|
||||
replace: "!0"
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
# Always Expand Roles
|
||||
|
||||
Always expands the role list in profile popouts
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
migratePluginSettings("AlwaysExpandRoles", "ShowAllRoles");
|
||||
export default definePlugin({
|
||||
name: "AlwaysExpandRoles",
|
||||
description: "Always expands the role list in profile popouts",
|
||||
authors: [Devs.surgedevs],
|
||||
patches: [
|
||||
{
|
||||
find: 'action:"EXPAND_ROLES"',
|
||||
replacement: [
|
||||
{
|
||||
match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/,
|
||||
replace: (_, rest, setExpandedRoles) => `${rest}!0)`
|
||||
},
|
||||
{
|
||||
// Fix not calculating non-expanded roles because the above patch makes the default "expanded",
|
||||
// which makes the collapse button never show up and calculation never occur
|
||||
match: /(?<=useLayoutEffect\(\(\)=>{if\()\i/,
|
||||
replace: isExpanded => "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
|
@ -21,12 +21,12 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||
import { findByProps, findComponentByCode } from "@webpack";
|
||||
|
||||
type AnonUpload = Upload & { anonymise?: boolean; };
|
||||
|
||||
const ActionBarIcon = findByCodeLazy(".actionBarIcon)");
|
||||
const UploadDraft = findByPropsLazy("popFirstFile", "update");
|
||||
const ActionBarIcon = findComponentByCode(".actionBarIcon)");
|
||||
const UploadDraft = findByProps("popFirstFile", "update");
|
||||
|
||||
const enum Methods {
|
||||
Random,
|
||||
|
@ -71,7 +71,7 @@ export default definePlugin({
|
|||
description: "Anonymise uploaded file names",
|
||||
patches: [
|
||||
{
|
||||
find: "instantBatchUpload:",
|
||||
find: "instantBatchUpload:function",
|
||||
replacement: {
|
||||
match: /uploadFiles:(\i),/,
|
||||
replace:
|
||||
|
@ -86,9 +86,9 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: "#{intl::ATTACHMENT_UTILITIES_SPOILER}",
|
||||
find: ".Messages.ATTACHMENT_UTILITIES_SPOILER",
|
||||
replacement: {
|
||||
match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}#{intl::ATTACHMENT_UTILITIES_SPOILER})/,
|
||||
match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}\i\.\i\.Messages\.ATTACHMENT_UTILITIES_SPOILER)/,
|
||||
replace: "arguments[0].canEdit!==false?$self.renderIcon(arguments[0]):null,"
|
||||
},
|
||||
},
|
||||
|
|
|
@ -24,7 +24,7 @@ interface ActivityButton {
|
|||
}
|
||||
|
||||
interface Activity {
|
||||
state?: string;
|
||||
state: string;
|
||||
details?: string;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
|
@ -52,8 +52,8 @@ const enum ActivityFlag {
|
|||
|
||||
export interface TrackData {
|
||||
name: string;
|
||||
album?: string;
|
||||
artist?: string;
|
||||
album: string;
|
||||
artist: string;
|
||||
|
||||
appleMusicLink?: string;
|
||||
songLink?: string;
|
||||
|
@ -61,8 +61,8 @@ export interface TrackData {
|
|||
albumArtwork?: string;
|
||||
artistArtwork?: string;
|
||||
|
||||
playerPosition?: number;
|
||||
duration?: number;
|
||||
playerPosition: number;
|
||||
duration: number;
|
||||
}
|
||||
|
||||
const enum AssetImageType {
|
||||
|
@ -120,7 +120,7 @@ const settings = definePluginSettings({
|
|||
stateString: {
|
||||
type: OptionType.STRING,
|
||||
description: "Activity state format string",
|
||||
default: "{artist} · {album}"
|
||||
default: "{artist}"
|
||||
},
|
||||
largeImageType: {
|
||||
type: OptionType.SELECT,
|
||||
|
@ -155,8 +155,8 @@ const settings = definePluginSettings({
|
|||
function customFormat(formatStr: string, data: TrackData) {
|
||||
return formatStr
|
||||
.replaceAll("{name}", data.name)
|
||||
.replaceAll("{album}", data.album ?? "")
|
||||
.replaceAll("{artist}", data.artist ?? "");
|
||||
.replaceAll("{album}", data.album)
|
||||
.replaceAll("{artist}", data.artist);
|
||||
}
|
||||
|
||||
function getImageAsset(type: AssetImageType, data: TrackData) {
|
||||
|
@ -212,16 +212,14 @@ export default definePlugin({
|
|||
|
||||
const assets: ActivityAssets = {};
|
||||
|
||||
const isRadio = Number.isNaN(trackData.duration) && (trackData.playerPosition === 0);
|
||||
|
||||
if (settings.store.largeImageType !== AssetImageType.Disabled) {
|
||||
assets.large_image = largeImageAsset;
|
||||
if (!isRadio) assets.large_text = customFormat(settings.store.largeTextString, trackData);
|
||||
assets.large_text = customFormat(settings.store.largeTextString, trackData);
|
||||
}
|
||||
|
||||
if (settings.store.smallImageType !== AssetImageType.Disabled) {
|
||||
assets.small_image = smallImageAsset;
|
||||
if (!isRadio) assets.small_text = customFormat(settings.store.smallTextString, trackData);
|
||||
assets.small_text = customFormat(settings.store.smallTextString, trackData);
|
||||
}
|
||||
|
||||
const buttons: ActivityButton[] = [];
|
||||
|
@ -245,17 +243,17 @@ export default definePlugin({
|
|||
|
||||
name: customFormat(settings.store.nameString, trackData),
|
||||
details: customFormat(settings.store.detailsString, trackData),
|
||||
state: isRadio ? undefined : customFormat(settings.store.stateString, trackData),
|
||||
state: customFormat(settings.store.stateString, trackData),
|
||||
|
||||
timestamps: (trackData.playerPosition && trackData.duration && settings.store.enableTimestamps) ? {
|
||||
timestamps: (settings.store.enableTimestamps ? {
|
||||
start: Date.now() - (trackData.playerPosition * 1000),
|
||||
end: Date.now() - (trackData.playerPosition * 1000) + (trackData.duration * 1000),
|
||||
} : undefined,
|
||||
} : undefined),
|
||||
|
||||
assets,
|
||||
|
||||
buttons: !isRadio && buttons.length ? buttons.map(v => v.label) : undefined,
|
||||
metadata: !isRadio && buttons.length ? { button_urls: buttons.map(v => v.url) } : undefined,
|
||||
buttons: buttons.length ? buttons.map(v => v.label) : undefined,
|
||||
metadata: { button_urls: buttons.map(v => v.url) || undefined, },
|
||||
|
||||
type: settings.store.activityType,
|
||||
flags: ActivityFlag.INSTANCE,
|
||||
|
|
|
@ -11,11 +11,37 @@ import type { TrackData } from ".";
|
|||
|
||||
const exec = promisify(execFile);
|
||||
|
||||
// function exec(file: string, args: string[] = []) {
|
||||
// return new Promise<{ code: number | null, stdout: string | null, stderr: string | null; }>((resolve, reject) => {
|
||||
// const process = spawn(file, args, { stdio: [null, "pipe", "pipe"] });
|
||||
|
||||
// let stdout: string | null = null;
|
||||
// process.stdout.on("data", (chunk: string) => { stdout ??= ""; stdout += chunk; });
|
||||
// let stderr: string | null = null;
|
||||
// process.stderr.on("data", (chunk: string) => { stdout ??= ""; stderr += chunk; });
|
||||
|
||||
// process.on("exit", code => { resolve({ code, stdout, stderr }); });
|
||||
// process.on("error", err => reject(err));
|
||||
// });
|
||||
// }
|
||||
|
||||
async function applescript(cmds: string[]) {
|
||||
const { stdout } = await exec("osascript", cmds.map(c => ["-e", c]).flat());
|
||||
return stdout;
|
||||
}
|
||||
|
||||
function makeSearchUrl(type: string, query: string) {
|
||||
const url = new URL("https://tools.applemediaservices.com/api/apple-media/music/US/search.json");
|
||||
url.searchParams.set("types", type);
|
||||
url.searchParams.set("limit", "1");
|
||||
url.searchParams.set("term", query);
|
||||
return url;
|
||||
}
|
||||
|
||||
const requestOptions: RequestInit = {
|
||||
headers: { "user-agent": "Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0" },
|
||||
};
|
||||
|
||||
interface RemoteData {
|
||||
appleMusicLink?: string,
|
||||
songLink?: string,
|
||||
|
@ -25,24 +51,6 @@ interface RemoteData {
|
|||
|
||||
let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null;
|
||||
|
||||
const APPLE_MUSIC_BUNDLE_REGEX = /<script type="module" crossorigin src="([a-zA-Z0-9.\-/]+)"><\/script>/;
|
||||
const APPLE_MUSIC_TOKEN_REGEX = /\w+="([A-Za-z0-9-_]*\.[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*)",\w+="x-apple-jingle-correlation-key"/;
|
||||
|
||||
let cachedToken: string | undefined = undefined;
|
||||
|
||||
const getToken = async () => {
|
||||
if (cachedToken) return cachedToken;
|
||||
|
||||
const html = await fetch("https://music.apple.com/").then(r => r.text());
|
||||
const bundleUrl = new URL(html.match(APPLE_MUSIC_BUNDLE_REGEX)![1], "https://music.apple.com/");
|
||||
|
||||
const bundle = await fetch(bundleUrl).then(r => r.text());
|
||||
const token = bundle.match(APPLE_MUSIC_TOKEN_REGEX)![1];
|
||||
|
||||
cachedToken = token;
|
||||
return token;
|
||||
};
|
||||
|
||||
async function fetchRemoteData({ id, name, artist, album }: { id: string, name: string, artist: string, album: string; }) {
|
||||
if (id === cachedRemoteData?.id) {
|
||||
if ("data" in cachedRemoteData) return cachedRemoteData.data;
|
||||
|
@ -50,39 +58,21 @@ async function fetchRemoteData({ id, name, artist, album }: { id: string, name:
|
|||
}
|
||||
|
||||
try {
|
||||
const dataUrl = new URL("https://amp-api-edge.music.apple.com/v1/catalog/us/search");
|
||||
dataUrl.searchParams.set("platform", "web");
|
||||
dataUrl.searchParams.set("l", "en-US");
|
||||
dataUrl.searchParams.set("limit", "1");
|
||||
dataUrl.searchParams.set("with", "serverBubbles");
|
||||
dataUrl.searchParams.set("types", "songs");
|
||||
dataUrl.searchParams.set("term", `${name} ${artist} ${album}`);
|
||||
dataUrl.searchParams.set("include[songs]", "artists");
|
||||
const [songData, artistData] = await Promise.all([
|
||||
fetch(makeSearchUrl("songs", artist + " " + album + " " + name), requestOptions).then(r => r.json()),
|
||||
fetch(makeSearchUrl("artists", artist.split(/ *[,&] */)[0]), requestOptions).then(r => r.json())
|
||||
]);
|
||||
|
||||
const token = await getToken();
|
||||
const appleMusicLink = songData?.songs?.data[0]?.attributes.url;
|
||||
const songLink = songData?.songs?.data[0]?.id ? `https://song.link/i/${songData?.songs?.data[0]?.id}` : undefined;
|
||||
|
||||
const songData = await fetch(dataUrl, {
|
||||
headers: {
|
||||
"accept": "*/*",
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
"authorization": `Bearer ${token}`,
|
||||
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
|
||||
"origin": "https://music.apple.com",
|
||||
},
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => data.results.song.data[0]);
|
||||
const albumArtwork = songData?.songs?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512");
|
||||
const artistArtwork = artistData?.artists?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512");
|
||||
|
||||
cachedRemoteData = {
|
||||
id,
|
||||
data: {
|
||||
appleMusicLink: songData.attributes.url,
|
||||
songLink: `https://song.link/i/${songData.id}`,
|
||||
albumArtwork: songData.attributes.artwork.url.replace("{w}x{h}", "512x512"),
|
||||
artistArtwork: songData.relationships.artists.data[0].attributes.artwork.url.replace("{w}x{h}", "512x512"),
|
||||
}
|
||||
data: { appleMusicLink, songLink, albumArtwork, artistArtwork }
|
||||
};
|
||||
|
||||
return cachedRemoteData.data;
|
||||
} catch (e) {
|
||||
console.error("[AppleMusicRichPresence] Failed to fetch remote data:", e);
|
||||
|
|
|
@ -20,10 +20,10 @@ import { popNotice, showNotice } from "@api/Notices";
|
|||
import { Link } from "@components/Link";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { ReporterTestable } from "@utils/types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { findByCode } from "@webpack";
|
||||
import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common";
|
||||
|
||||
const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID");
|
||||
const fetchApplicationsRPC = findByCode("APPLICATION_RPC(", "Client ID");
|
||||
|
||||
async function lookupAsset(applicationId: string, key: string): Promise<string> {
|
||||
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
||||
|
@ -73,8 +73,8 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
async start() {
|
||||
// Legcord comes with its own arRPC implementation, so this plugin just confuses users
|
||||
if ("legcord" in window) return;
|
||||
// ArmCord comes with its own arRPC implementation, so this plugin just confuses users
|
||||
if ("armcord" in window) return;
|
||||
|
||||
if (ws) ws.close();
|
||||
ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket
|
||||
|
|
|
@ -36,7 +36,7 @@ export default definePlugin({
|
|||
settings,
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::BAN_CONFIRM_TITLE}",
|
||||
find: "BAN_CONFIRM_TITLE.",
|
||||
replacement: {
|
||||
match: /src:\i\("?\d+"?\)/g,
|
||||
replace: "src:$self.source"
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { findByProps, findComponentByCode, findStore } from "@webpack";
|
||||
import { useStateFromStores } from "@webpack/common";
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
import { ExpandedGuildFolderStore, settings } from ".";
|
||||
|
||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
||||
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
||||
const GuildsBar = findComponentByCodeLazy('("guildsnav")');
|
||||
const ChannelRTCStore = findStore("ChannelRTCStore");
|
||||
const Animations = findByProps("a", "animated", "useTransition");
|
||||
const GuildsBar = findComponentByCode('("guildsnav")');
|
||||
|
||||
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
# Better Folders
|
||||
|
||||
Better Folders offers a variety of options to improve your folder experience
|
||||
|
||||
Always show the folder icon, regardless of if the folder is open or not
|
||||
|
||||
Only have one folder open at a time
|
||||
|
||||
Open folders in a sidebar:
|
||||
|
||||
![A folder open in a separate sidebar](https://github.com/user-attachments/assets/432d3146-8091-4bae-9c1e-c19046c72947)
|
|
@ -18,10 +18,9 @@
|
|||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
||||
import { FluxDispatcher, useMemo } from "@webpack/common";
|
||||
import { find, findByProps, findStore } from "@webpack";
|
||||
import { FluxDispatcher, i18n, useMemo } from "@webpack/common";
|
||||
|
||||
import FolderSideBar from "./FolderSideBar";
|
||||
|
||||
|
@ -31,10 +30,10 @@ enum FolderIconDisplay {
|
|||
MoreThanOneFolderExpanded
|
||||
}
|
||||
|
||||
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
||||
const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
||||
const GuildsTree = findLazy(m => m.prototype?.moveNextTo);
|
||||
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
||||
const GuildsTree = find(m => m.prototype?.moveNextTo);
|
||||
const SortedGuildStore = findStore("SortedGuildStore");
|
||||
export const ExpandedGuildFolderStore = findStore("ExpandedGuildFolderStore");
|
||||
const FolderUtils = findByProps("move", "toggleGuildFolderExpand");
|
||||
|
||||
let lastGuildId = null as string | null;
|
||||
let dispatchingFoldersClose = false;
|
||||
|
@ -119,22 +118,22 @@ export default definePlugin({
|
|||
// If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders
|
||||
{
|
||||
match: /\[(\i)\]=(\(0,\i\.\i\).{0,40}getGuildsTree\(\).+?}\))(?=,)/,
|
||||
replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0]?.isBetterFolders,betterFoldersOriginalTree,arguments[0]?.betterFoldersExpandedIds)`
|
||||
replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0].isBetterFolders,betterFoldersOriginalTree,arguments[0].betterFoldersExpandedIds)`
|
||||
},
|
||||
// If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children
|
||||
{
|
||||
match: /lastTargetNode:\i\[\i\.length-1\].+?Fragment.+?\]}\)\]/,
|
||||
replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0]?.isBetterFolders))"
|
||||
replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0].isBetterFolders))"
|
||||
},
|
||||
// If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children
|
||||
{
|
||||
match: /unreadMentionsIndicatorBottom,.+?}\)\]/,
|
||||
replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0]?.isBetterFolders))"
|
||||
replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0].isBetterFolders))"
|
||||
},
|
||||
// Export the isBetterFolders variable to the folders component
|
||||
{
|
||||
match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/,
|
||||
replace: '$&isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
|
||||
match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,)/,
|
||||
replace: 'isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -168,31 +167,31 @@ export default definePlugin({
|
|||
{
|
||||
predicate: () => settings.store.keepIcons,
|
||||
match: /(?<=let{folderNode:\i,setNodeRef:\i,.+?expanded:(\i),.+?;)(?=let)/,
|
||||
replace: (_, isExpanded) => `${isExpanded}=!!arguments[0]?.isBetterFolders&&${isExpanded};`
|
||||
replace: (_, isExpanded) => `${isExpanded}=!!arguments[0].isBetterFolders&&${isExpanded};`
|
||||
},
|
||||
// Disable expanding and collapsing folders transition in the normal GuildsBar sidebar
|
||||
{
|
||||
predicate: () => !settings.store.keepIcons,
|
||||
match: /(?<=#{intl::SERVER_FOLDER_PLACEHOLDER}.+?useTransition\)\()/,
|
||||
replace: "$self.shouldShowTransition(arguments[0])&&"
|
||||
match: /(?<=\.Messages\.SERVER_FOLDER_PLACEHOLDER.+?useTransition\)\()/,
|
||||
replace: "!!arguments[0].isBetterFolders&&"
|
||||
},
|
||||
// If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded
|
||||
{
|
||||
predicate: () => !settings.store.keepIcons,
|
||||
match: /expandedFolderBackground,.+?,(?=\i\(\(\i,\i,\i\)=>{let{key.{0,45}ul)(?<=selected:\i,expanded:(\i),.+?)/,
|
||||
replace: (m, isExpanded) => `${m}$self.shouldRenderContents(arguments[0],${isExpanded})?null:`
|
||||
replace: (m, isExpanded) => `${m}!arguments[0].isBetterFolders&&${isExpanded}?null:`
|
||||
},
|
||||
{
|
||||
// Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar
|
||||
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
||||
match: /(?<=\.wrapper,children:\[)/,
|
||||
replace: "$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)&&"
|
||||
replace: "$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)&&"
|
||||
},
|
||||
{
|
||||
// Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar
|
||||
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
||||
match: /(?<=\.expandedFolderBackground.+?}\),)(?=\i,)/,
|
||||
replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)?null:"
|
||||
replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)?null:"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -201,12 +200,12 @@ export default definePlugin({
|
|||
predicate: () => settings.store.sidebar,
|
||||
replacement: {
|
||||
// Render the Better Folders sidebar
|
||||
match: /(container.{0,50}({className:\i\.guilds,themeOverride:\i})\))/,
|
||||
replace: "$1,$self.FolderSideBar({...$2})"
|
||||
match: /(?<=({className:\i\.guilds,themeOverride:\i})\))/,
|
||||
replace: ",$self.FolderSideBar($1)"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "#{intl::DISCODO_DISABLED}",
|
||||
find: ".Messages.DISCODO_DISABLED",
|
||||
predicate: () => settings.store.closeAllHomeButton,
|
||||
replacement: {
|
||||
// Close all folders when clicking the home button
|
||||
|
@ -275,16 +274,12 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
|
||||
return child => {
|
||||
if (isBetterFolders) {
|
||||
try {
|
||||
return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return child => {
|
||||
if (isBetterFolders) {
|
||||
return child?.props?.["aria-label"] === i18n.Messages.SERVERS;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
},
|
||||
|
||||
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
|
||||
|
@ -311,20 +306,7 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
|
||||
shouldShowTransition(props: any) {
|
||||
// Pending guilds
|
||||
if (props?.folderNode?.id === 1) return true;
|
||||
FolderSideBar: guildsBarProps => <FolderSideBar {...guildsBarProps} />,
|
||||
|
||||
return !!props?.isBetterFolders;
|
||||
},
|
||||
|
||||
shouldRenderContents(props: any, isExpanded: boolean) {
|
||||
// Pending guilds
|
||||
if (props?.folderNode?.id === 1) return false;
|
||||
|
||||
return !props?.isBetterFolders && isExpanded;
|
||||
},
|
||||
|
||||
FolderSideBar,
|
||||
closeFolders,
|
||||
closeFolders
|
||||
});
|
||||
|
|
|
@ -34,9 +34,9 @@ export default definePlugin({
|
|||
},
|
||||
},
|
||||
{
|
||||
find: "#{intl::GIF}",
|
||||
find: ".Messages.GIF,",
|
||||
replacement: {
|
||||
match: /alt:(\i)=(\i\.\i\.string\(\i\.\i#{intl::GIF}\))(?=,[^}]*\}=(\i))/,
|
||||
match: /alt:(\i)=(\i\.\i\.Messages\.GIF)(?=,[^}]*\}=(\i))/,
|
||||
replace:
|
||||
// rename prop so we can always use default value
|
||||
"alt_$$:$1=$self.altify($3)||$2",
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
@ -31,7 +31,7 @@ const settings = definePluginSettings({
|
|||
noSpellCheck: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Disable spellcheck in notes",
|
||||
disabled: () => Settings.plugins.BetterNotesBox.hide,
|
||||
disabled: () => settings.store.hide,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
@ -63,9 +63,9 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: "#{intl::NOTE_PLACEHOLDER}",
|
||||
find: "Messages.NOTE_PLACEHOLDER",
|
||||
replacement: {
|
||||
match: /#{intl::NOTE_PLACEHOLDER}\),/,
|
||||
match: /\.NOTE_PLACEHOLDER,/,
|
||||
replace: "$&spellCheck:!$self.noSpellCheck,"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@ import { ImageIcon } from "@components/Icons";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentGuild, openImageModal } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common";
|
||||
|
||||
const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild");
|
||||
const GuildSettingsActions = findByProps("open", "selectRole", "updateGuild");
|
||||
|
||||
const DeveloperMode = getUserSettingLazy("appearance", "developerMode")!;
|
||||
const DeveloperMode = getUserSettingLazy("appearance", "developerMode");
|
||||
|
||||
function PencilIcon() {
|
||||
return (
|
||||
|
@ -99,11 +99,7 @@ export default definePlugin({
|
|||
id="vc-view-role-icon"
|
||||
label="View Role Icon"
|
||||
action={() => {
|
||||
openImageModal({
|
||||
url: `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${role.id}/${role.icon}.${settings.store.roleIconFileFormat}`,
|
||||
height: 128,
|
||||
width: 128
|
||||
});
|
||||
openImageModal(`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${role.id}/${role.icon}.${settings.store.roleIconFileFormat}`);
|
||||
}}
|
||||
icon={ImageIcon}
|
||||
/>
|
||||
|
|
|
@ -16,16 +16,32 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Clipboard, Toasts } from "@webpack/common";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
bothStyles: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show both role dot and coloured names",
|
||||
restartNeeded: true,
|
||||
default: false,
|
||||
},
|
||||
copyRoleColorInProfilePopout: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Allow click on role dot in profile popout to copy role color",
|
||||
restartNeeded: true,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "BetterRoleDot",
|
||||
authors: [Devs.Ven, Devs.AutumnVN],
|
||||
description:
|
||||
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
|
@ -39,7 +55,7 @@ export default definePlugin({
|
|||
find: '"dot"===',
|
||||
all: true,
|
||||
noWarn: true,
|
||||
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
|
||||
predicate: () => settings.store.bothStyles,
|
||||
replacement: {
|
||||
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
||||
replace: "true",
|
||||
|
@ -47,9 +63,9 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
{
|
||||
find: "#{intl::ADD_ROLE_A11Y_LABEL}",
|
||||
find: ".ADD_ROLE_A11Y_LABEL",
|
||||
all: true,
|
||||
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||
predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
|
||||
noWarn: true,
|
||||
replacement: {
|
||||
match: /"dot"===\i/,
|
||||
|
@ -59,7 +75,7 @@ export default definePlugin({
|
|||
{
|
||||
find: ".roleVerifiedIcon",
|
||||
all: true,
|
||||
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||
predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
|
||||
noWarn: true,
|
||||
replacement: {
|
||||
match: /"dot"===\i/,
|
||||
|
@ -68,21 +84,6 @@ export default definePlugin({
|
|||
}
|
||||
],
|
||||
|
||||
options: {
|
||||
bothStyles: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show both role dot and coloured names",
|
||||
restartNeeded: true,
|
||||
default: false,
|
||||
},
|
||||
copyRoleColorInProfilePopout: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Allow click on role dot in profile popout to copy role color",
|
||||
restartNeeded: true,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
copyToClipBoard(color: string) {
|
||||
Clipboard.copy(color);
|
||||
Toasts.show({
|
||||
|
|
|
@ -21,20 +21,20 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
|
||||
import { findByProps, findExportedComponent, findStore } from "@webpack";
|
||||
import { Constants, React, RestAPI, Tooltip } from "@webpack/common";
|
||||
|
||||
import { RenameButton } from "./components/RenameButton";
|
||||
import { Session, SessionInfo } from "./types";
|
||||
import { fetchNamesFromDataStore, getDefaultName, GetOsColor, GetPlatformIcon, savedSessionsCache, saveSessionsToDataStore } from "./utils";
|
||||
|
||||
const AuthSessionsStore = findStoreLazy("AuthSessionsStore");
|
||||
const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open");
|
||||
const AuthSessionsStore = findStore("AuthSessionsStore");
|
||||
const UserSettingsModal = findByProps("saveAccountChanges", "open");
|
||||
|
||||
const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer");
|
||||
const SessionIconClasses = findByPropsLazy("sessionIcon");
|
||||
const TimestampClasses = findByProps("timestampTooltip", "blockquoteContainer");
|
||||
const SessionIconClasses = findByProps("sessionIcon");
|
||||
|
||||
const BlobMask = findExportedComponentLazy("BlobMask");
|
||||
const BlobMask = findExportedComponent("BlobMask");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
backgroundCheck: {
|
||||
|
@ -60,7 +60,7 @@ export default definePlugin({
|
|||
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::AUTH_SESSIONS_SESSION_LOG_OUT}",
|
||||
find: "Messages.AUTH_SESSIONS_SESSION_LOG_OUT",
|
||||
replacement: [
|
||||
// Replace children with a single label with state
|
||||
{
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { openPluginModal } from "@components/PluginSettings/PluginModal";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { isObjectEmpty } from "@utils/misc";
|
||||
import { Alerts, Menu, useMemo, useState } from "@webpack/common";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
function onRestartNeeded() {
|
||||
Alerts.show({
|
||||
title: "Restart required",
|
||||
body: <p>You have changed settings that require a restart.</p>,
|
||||
confirmText: "Restart now",
|
||||
cancelText: "Later!",
|
||||
onConfirm: () => location.reload()
|
||||
});
|
||||
}
|
||||
|
||||
export default function PluginsSubmenu() {
|
||||
const sortedPlugins = useMemo(() => Object.values(Plugins)
|
||||
.sort((a, b) => a.name.localeCompare(b.name)), []);
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
const search = query.toLowerCase();
|
||||
const include = (p: typeof Plugins[keyof typeof Plugins]) => (
|
||||
Vencord.Plugins.isPluginEnabled(p.name)
|
||||
&& p.options && !isObjectEmpty(p.options)
|
||||
&& (
|
||||
p.name.toLowerCase().includes(search)
|
||||
|| p.description.toLowerCase().includes(search)
|
||||
|| p.tags?.some(t => t.toLowerCase().includes(search))
|
||||
)
|
||||
);
|
||||
|
||||
const plugins = sortedPlugins.filter(include);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu.MenuControlItem
|
||||
id="vc-plugins-search"
|
||||
control={(props, ref) => (
|
||||
<Menu.MenuSearchControl
|
||||
{...props}
|
||||
query={query}
|
||||
onChange={setQuery}
|
||||
ref={ref}
|
||||
placeholder={getIntlMessage("SEARCH")}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{!!plugins.length && <Menu.MenuSeparator />}
|
||||
|
||||
{plugins.map(p => (
|
||||
<Menu.MenuItem
|
||||
key={p.name}
|
||||
id={p.name}
|
||||
label={p.name}
|
||||
action={() => openPluginModal(p, onRestartNeeded)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -7,20 +7,18 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
|
||||
import { NoopComponent } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { waitFor } from "@webpack";
|
||||
import { ComponentDispatch, FocusLock, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import { findByProps } from "@webpack";
|
||||
import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import type { HTMLAttributes, ReactElement } from "react";
|
||||
|
||||
import PluginsSubmenu from "./PluginsSubmenu";
|
||||
|
||||
type SettingsEntry = { section: string, label: string; };
|
||||
|
||||
const cl = classNameFactory("");
|
||||
let Classes: Record<string, string>;
|
||||
waitFor(["animating", "baseLayer", "bg", "layer", "layers"], m => Classes = m);
|
||||
const Classes = findByProps("animating", "baseLayer", "bg", "layer", "layers");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
disableFade: {
|
||||
|
@ -112,7 +110,7 @@ export default definePlugin({
|
|||
predicate: () => settings.store.disableFade
|
||||
},
|
||||
{ // Load menu TOC eagerly
|
||||
find: "#{intl::USER_SETTINGS_WITH_BUILD_OVERRIDE}",
|
||||
find: "Messages.USER_SETTINGS_WITH_BUILD_OVERRIDE.format",
|
||||
replacement: {
|
||||
match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?null!=\i&&.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/,
|
||||
replace: "$&(async ()=>$2)(),"
|
||||
|
@ -120,22 +118,14 @@ export default definePlugin({
|
|||
predicate: () => settings.store.eagerLoad
|
||||
},
|
||||
{ // Settings cog context menu
|
||||
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
||||
replacement: [
|
||||
{
|
||||
match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
|
||||
replace: "$1$self.wrapMenu($2)"
|
||||
},
|
||||
{
|
||||
match: /case \i\.\i\.DEVELOPER_OPTIONS:return \i;/,
|
||||
replace: "$&case 'VencordPlugins':return $self.PluginsSubmenu();"
|
||||
}
|
||||
]
|
||||
},
|
||||
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL",
|
||||
replacement: {
|
||||
match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
|
||||
replace: "$1$self.wrapMenu($2)"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
PluginsSubmenu,
|
||||
|
||||
// This is the very outer layer of the entire ui, so we can't wrap this in an ErrorBoundary
|
||||
// without possibly also catching unrelated errors of children.
|
||||
//
|
||||
|
@ -143,7 +133,7 @@ export default definePlugin({
|
|||
// try catch will only catch errors in the Layer function (hence why it's called as a plain function rather than a component), but
|
||||
// not in children
|
||||
Layer(props: LayerProps) {
|
||||
if (!FocusLock || !ComponentDispatch || !Classes) {
|
||||
if (FocusLock === NoopComponent || ComponentDispatch[SYM_PROXY_INNER_VALUE] == null || Classes[SYM_PROXY_INNER_VALUE] == null) {
|
||||
new Logger("BetterSettings").error("Failed to find some components");
|
||||
return props.children;
|
||||
}
|
||||
|
@ -160,7 +150,7 @@ export default definePlugin({
|
|||
if (item.section === "HEADER") {
|
||||
items.push({ label: item.label, items: [] });
|
||||
} else if (item.section === "DIVIDER") {
|
||||
items.push({ label: getIntlMessage("OTHER_OPTIONS"), items: [] });
|
||||
items.push({ label: i18n.Messages.OTHER_OPTIONS, items: [] });
|
||||
} else {
|
||||
items.at(-1)!.items.push(item);
|
||||
}
|
||||
|
|
|
@ -57,11 +57,7 @@ export const handleViewPreview = async ({ guildId, channelId, ownerId }: Applica
|
|||
const previewUrl = await ApplicationStreamPreviewStore.getPreviewURL(guildId, channelId, ownerId);
|
||||
if (!previewUrl) return;
|
||||
|
||||
openImageModal({
|
||||
url: previewUrl,
|
||||
height: 720,
|
||||
width: 1280
|
||||
});
|
||||
openImageModal(previewUrl);
|
||||
};
|
||||
|
||||
export const addViewStreamContext: NavContextMenuPatchCallback = (children, { userId }: { userId: string | bigint; }) => {
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { findStoreLazy } from "@webpack";
|
||||
import { findStore } from "@webpack";
|
||||
|
||||
import * as t from "./types/stores";
|
||||
|
||||
export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStoreLazy("ApplicationStreamPreviewStore");
|
||||
export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore");
|
||||
export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStore("ApplicationStreamPreviewStore");
|
||||
export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStore("ApplicationStreamingStore");
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
|
@ -26,7 +26,7 @@ function setCss() {
|
|||
style.textContent = `
|
||||
.vc-nsfw-img [class^=imageWrapper] img,
|
||||
.vc-nsfw-img [class^=wrapperPaused] video {
|
||||
filter: blur(${Settings.plugins.BlurNSFW.blurAmount}px);
|
||||
filter: blur(${settings.store.blurAmount}px);
|
||||
transition: filter 0.2s;
|
||||
}
|
||||
.vc-nsfw-img [class^=imageWrapper]:hover img,
|
||||
|
@ -36,30 +36,31 @@ function setCss() {
|
|||
`;
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
blurAmount: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Blur Amount",
|
||||
default: 10,
|
||||
onChange: setCss
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "BlurNSFW",
|
||||
description: "Blur attachments in NSFW channels until hovered",
|
||||
authors: [Devs.Ven],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: ".embedWrapper,embed",
|
||||
replacement: [{
|
||||
match: /\.container/,
|
||||
replace: "$&+(this.props.channel.nsfw? ' vc-nsfw-img': '')"
|
||||
match: /\.embedWrapper(?=.+?channel_id:(\i)\.id)/g,
|
||||
replace: "$&+($1.nsfw?' vc-nsfw-img':'')"
|
||||
}]
|
||||
}
|
||||
],
|
||||
|
||||
options: {
|
||||
blurAmount: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Blur Amount",
|
||||
default: 10,
|
||||
onChange: setCss
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
style = document.createElement("style");
|
||||
style.id = "VcBlurNsfw";
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { useTimer } from "@utils/react";
|
||||
|
@ -25,7 +25,7 @@ import { React } from "@webpack/common";
|
|||
|
||||
function formatDuration(ms: number) {
|
||||
// here be dragons (moment fucking sucks)
|
||||
const human = Settings.plugins.CallTimer.format === "human";
|
||||
const human = settings.store.format === "human";
|
||||
|
||||
const format = (n: number) => human ? n : n.toString().padStart(2, "0");
|
||||
const unit = (s: string) => human ? s : "";
|
||||
|
@ -46,32 +46,33 @@ function formatDuration(ms: number) {
|
|||
return res;
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
format: {
|
||||
type: OptionType.SELECT,
|
||||
description: "The timer format. This can be any valid moment.js format",
|
||||
options: [
|
||||
{
|
||||
label: "30d 23:00:42",
|
||||
value: "stopwatch",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "30d 23h 00m 42s",
|
||||
value: "human"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "CallTimer",
|
||||
description: "Adds a timer to vcs",
|
||||
authors: [Devs.Ven],
|
||||
settings,
|
||||
|
||||
startTime: 0,
|
||||
interval: void 0 as NodeJS.Timeout | undefined,
|
||||
|
||||
options: {
|
||||
format: {
|
||||
type: OptionType.SELECT,
|
||||
description: "The timer format. This can be any valid moment.js format",
|
||||
options: [
|
||||
{
|
||||
label: "30d 23:00:42",
|
||||
value: "stopwatch",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "30d 23h 00m 42s",
|
||||
value: "human"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
patches: [{
|
||||
find: "renderConnectionStatus(){",
|
||||
replacement: {
|
||||
|
|
|
@ -155,5 +155,4 @@ export const defaultRules = [
|
|||
"igshid",
|
||||
"igsh",
|
||||
"share_id@reddit.com",
|
||||
"si@soundcloud.com",
|
||||
];
|
||||
|
|
|
@ -11,10 +11,10 @@ import { Devs } from "@utils/constants";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common";
|
||||
import { findByCode, findComponentByCode, findStore } from "@webpack";
|
||||
import { Button, Forms, useStateFromStores } from "@webpack/common";
|
||||
|
||||
const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
|
||||
const ColorPicker = findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
|
||||
|
||||
const colorPresets = [
|
||||
"#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D",
|
||||
|
@ -30,20 +30,21 @@ function onPickColor(color: number) {
|
|||
updateColorVars(hexColor);
|
||||
}
|
||||
|
||||
const saveClientTheme = findByCodeLazy('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"===');
|
||||
const saveClientTheme = findByCode('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"===');
|
||||
|
||||
function setTheme(theme: string) {
|
||||
saveClientTheme({ theme });
|
||||
}
|
||||
|
||||
const NitroThemeStore = findStoreLazy("ClientThemesBackgroundStore");
|
||||
const ThemeStore = findStore("ThemeStore");
|
||||
const ClientThemesBackgroundStore = findStore("ClientThemesBackgroundStore");
|
||||
|
||||
function ThemeSettings() {
|
||||
const theme = useStateFromStores([ThemeStore], () => ThemeStore.theme);
|
||||
const isLightTheme = theme === "light";
|
||||
const oppositeTheme = isLightTheme ? "dark" : "light";
|
||||
|
||||
const nitroTheme = useStateFromStores([NitroThemeStore], () => NitroThemeStore.gradientPreset);
|
||||
const nitroTheme = useStateFromStores([ClientThemesBackgroundStore], () => ClientThemesBackgroundStore.gradientPreset);
|
||||
const nitroThemeEnabled = nitroTheme !== undefined;
|
||||
|
||||
const selectedLuminance = relativeLuminance(settings.store.color);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# ConsoleJanitor
|
||||
|
||||
Disables annoying console messages/errors. This plugin mainly removes errors/warnings that happen all the time and Discord logger messages.
|
||||
Disables annoying console messages/errors. This plugin mainly removes errors/warnings that happen all the time and noisy/spammy logging messages.
|
||||
|
||||
One of the disabled messages is the "Window state not initialized" warning, for example.
|
||||
Some of the disabled messages include the "notosans-400-normalitalic" error and MessageActionCreators, Routing/Utils loggers.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
const Noop = () => { };
|
||||
const NoopLogger = {
|
||||
|
@ -22,12 +22,10 @@ const NoopLogger = {
|
|||
fileOnly: Noop
|
||||
};
|
||||
|
||||
const logAllow = new Set();
|
||||
|
||||
const settings = definePluginSettings({
|
||||
disableLoggers: {
|
||||
disableNoisyLoggers: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Disables Discords loggers",
|
||||
description: "Disable noisy loggers like the MessageActionCreators",
|
||||
default: false,
|
||||
restartNeeded: true
|
||||
},
|
||||
|
@ -36,43 +34,18 @@ const settings = definePluginSettings({
|
|||
description: "Disable the Spotify logger, which leaks account information and access token",
|
||||
default: true,
|
||||
restartNeeded: true
|
||||
},
|
||||
whitelistedLoggers: {
|
||||
type: OptionType.STRING,
|
||||
description: "Semi colon separated list of loggers to allow even if others are hidden",
|
||||
default: "GatewaySocket; Routing/Utils",
|
||||
onChange(newVal: string) {
|
||||
logAllow.clear();
|
||||
newVal.split(";").map(x => x.trim()).forEach(logAllow.add.bind(logAllow));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "ConsoleJanitor",
|
||||
description: "Disables annoying console messages/errors",
|
||||
authors: [Devs.Nuckyz, Devs.sadan],
|
||||
authors: [Devs.Nuckyz],
|
||||
settings,
|
||||
|
||||
startAt: StartAt.Init,
|
||||
start() {
|
||||
logAllow.clear();
|
||||
this.settings.store.whitelistedLoggers?.split(";").map(x => x.trim()).forEach(logAllow.add.bind(logAllow));
|
||||
},
|
||||
|
||||
NoopLogger: () => NoopLogger,
|
||||
shouldLog(logger: string) {
|
||||
return logAllow.has(logger);
|
||||
},
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: 'react-spring: The "interpolate" function',
|
||||
replacement: {
|
||||
match: /,console.warn\('react-spring: The "interpolate" function is deprecated in v10 \(use "to" instead\)'\)/,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
find: 'console.warn("Window state not initialized"',
|
||||
replacement: {
|
||||
|
@ -87,6 +60,13 @@ export default definePlugin({
|
|||
replace: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "notosans-400-normalitalic",
|
||||
replacement: {
|
||||
match: /,"notosans-.+?"/g,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
find: 'console.warn("[DEPRECATED] Please use `subscribeWithSelector` middleware");',
|
||||
all: true,
|
||||
|
@ -130,34 +110,34 @@ export default definePlugin({
|
|||
replace: ""
|
||||
}
|
||||
},
|
||||
// Zustand section
|
||||
{
|
||||
find: "[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.",
|
||||
replacement: [
|
||||
{
|
||||
match: /&&console\.warn\("\[DEPRECATED\] Passing a vanilla store will be unsupported in a future version\. Instead use `import { useStore } from 'zustand'`\."\)/,
|
||||
replace: ""
|
||||
},
|
||||
{
|
||||
match: /console\.warn\("\[DEPRECATED\] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`\. They can be imported from 'zustand\/traditional'\. https:\/\/github\.com\/pmndrs\/zustand\/discussions\/1937"\),/,
|
||||
replace: ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: "[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead.",
|
||||
...[
|
||||
'("MessageActionCreators")', '("ChannelMessages")',
|
||||
'("Routing/Utils")', '("RTCControlSocket")',
|
||||
'("ConnectionEventFramerateReducer")', '("RTCLatencyTestManager")',
|
||||
'("OverlayBridgeStore")', '("RPCServer:WSS")', '("RPCServer:IPC")'
|
||||
].map(logger => ({
|
||||
find: logger,
|
||||
predicate: () => settings.store.disableNoisyLoggers,
|
||||
all: true,
|
||||
replacement: {
|
||||
match: /console\.warn\("\[DEPRECATED\] `getStorage`, `serialize` and `deserialize` options are deprecated\. Use `storage` option instead\."\),/,
|
||||
replace: ""
|
||||
match: new RegExp(String.raw`new \i\.\i${logger.replace(/([()])/g, "\\$1")}`),
|
||||
replace: `$self.NoopLogger${logger}`
|
||||
}
|
||||
})),
|
||||
{
|
||||
find: '"Experimental codecs: "',
|
||||
predicate: () => settings.store.disableNoisyLoggers,
|
||||
replacement: {
|
||||
match: /new \i\.\i\("Connection\("\.concat\(\i,"\)"\)\)/,
|
||||
replace: "$self.NoopLogger()"
|
||||
}
|
||||
},
|
||||
// Patches discords generic logger function
|
||||
{
|
||||
find: "Σ:",
|
||||
predicate: () => settings.store.disableLoggers,
|
||||
find: '"Handling ping: "',
|
||||
predicate: () => settings.store.disableNoisyLoggers,
|
||||
replacement: {
|
||||
match: /(?<=&&)(?=console)/,
|
||||
replace: "$self.shouldLog(arguments[0])&&"
|
||||
match: /new \i\.\i\("RTCConnection\("\.concat.+?\)\)(?=,)/,
|
||||
replace: "$self.NoopLogger()"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -18,13 +18,14 @@
|
|||
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentChannel, getCurrentGuild } from "@utils/discord";
|
||||
import { runtimeHashMessageKey } from "@utils/intlHash";
|
||||
import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy";
|
||||
import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
|
||||
import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
|
||||
import definePlugin, { PluginNative, StartAt } from "@utils/types";
|
||||
import * as Webpack from "@webpack";
|
||||
import { extract, filters, findAll, findModuleId, search } from "@webpack";
|
||||
import { cacheFindAll, cacheFindModuleId, extract, filters, searchFactories } from "@webpack";
|
||||
import * as Common from "@webpack/common";
|
||||
import { loadLazyChunks } from "debug/loadLazyChunks";
|
||||
import type { ComponentType } from "react";
|
||||
|
@ -33,27 +34,29 @@ const DESKTOP_ONLY = (f: string) => () => {
|
|||
throw new Error(`'${f}' is Discord Desktop only.`);
|
||||
};
|
||||
|
||||
const define: typeof Object.defineProperty =
|
||||
(obj, prop, desc) => {
|
||||
if (Object.hasOwn(desc, "value"))
|
||||
desc.writable = true;
|
||||
|
||||
return Object.defineProperty(obj, prop, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
...desc
|
||||
});
|
||||
};
|
||||
type Define = typeof Object.defineProperty;
|
||||
const define: Define = (target, p, attributes) => {
|
||||
if (Object.hasOwn(attributes, "value")) {
|
||||
attributes.writable = true;
|
||||
}
|
||||
|
||||
return Object.defineProperty(target, p, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
...attributes
|
||||
});
|
||||
};
|
||||
|
||||
function makeShortcuts() {
|
||||
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) {
|
||||
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn, shouldReturnFactory = false) {
|
||||
const cache = new Map<string, unknown>();
|
||||
|
||||
return function (...filterProps: unknown[]) {
|
||||
const cacheKey = String(filterProps);
|
||||
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
||||
|
||||
const matches = findAll(filterFactory(...filterProps));
|
||||
const matches = cacheFindAll(filterFactory(...filterProps), shouldReturnFactory);
|
||||
|
||||
const result = (() => {
|
||||
switch (matches.length) {
|
||||
|
@ -61,51 +64,71 @@ function makeShortcuts() {
|
|||
case 1: return matches[0];
|
||||
default:
|
||||
const uniqueMatches = [...new Set(matches)];
|
||||
if (uniqueMatches.length > 1)
|
||||
if (uniqueMatches.length > 1) {
|
||||
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
|
||||
}
|
||||
|
||||
return matches[0];
|
||||
return uniqueMatches[0];
|
||||
}
|
||||
})();
|
||||
|
||||
if (result && cacheKey) cache.set(cacheKey, result);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
let fakeRenderWin: WeakRef<Window> | undefined;
|
||||
|
||||
const find = newFindWrapper(f => f);
|
||||
const findByProps = newFindWrapper(filters.byProps);
|
||||
|
||||
return {
|
||||
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
|
||||
wp: Webpack,
|
||||
wpc: { getter: () => Webpack.cache },
|
||||
wreq: { getter: () => Webpack.wreq },
|
||||
wpsearch: search,
|
||||
wpex: extract,
|
||||
wpexs: (code: string) => extract(findModuleId(code)!),
|
||||
|
||||
WebpackInstances: { getter: () => Vencord.WebpackPatcher.allWebpackInstances },
|
||||
loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); },
|
||||
|
||||
wpsearch: searchFactories,
|
||||
wpex: extract,
|
||||
wpexs: (...code: Webpack.CodeFilter) => extract(cacheFindModuleId(...code)!),
|
||||
|
||||
filters,
|
||||
find,
|
||||
findAll: findAll,
|
||||
findByProps,
|
||||
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
|
||||
findByCode: newFindWrapper(filters.byCode),
|
||||
findAllByCode: (code: string) => findAll(filters.byCode(code)),
|
||||
findAll: cacheFindAll,
|
||||
findComponent: find,
|
||||
findAllComponents: cacheFindAll,
|
||||
findExportedComponent: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
|
||||
findComponentByCode: newFindWrapper(filters.componentByCode),
|
||||
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
|
||||
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
|
||||
findAllComponentsByCode: (...code: Webpack.PropsFilter) => cacheFindAll(filters.componentByCode(...code)),
|
||||
findComponentByFields: newFindWrapper(filters.componentByFields),
|
||||
findAllComponentsByFields: (...fields: Webpack.PropsFilter) => cacheFindAll(filters.componentByFields(...fields)),
|
||||
findByProps,
|
||||
findAllByProps: (...props: Webpack.PropsFilter) => cacheFindAll(filters.byProps(...props)),
|
||||
findProp: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
|
||||
findByCode: newFindWrapper(filters.byCode),
|
||||
findAllByCode: (code: Webpack.CodeFilter) => cacheFindAll(filters.byCode(...code)),
|
||||
findStore: newFindWrapper(filters.byStoreName),
|
||||
PluginsApi: { getter: () => Vencord.Plugins },
|
||||
findByFactoryCode: newFindWrapper(filters.byFactoryCode),
|
||||
findAllByFactoryCode: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code)),
|
||||
findModuleFactory: newFindWrapper(filters.byFactoryCode, true),
|
||||
findAllModuleFactories: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code), true),
|
||||
|
||||
plugins: { getter: () => Vencord.Plugins.plugins },
|
||||
PluginsApi: { getter: () => Vencord.Plugins },
|
||||
Settings: { getter: () => Vencord.Settings },
|
||||
Api: { getter: () => Vencord.Api },
|
||||
Util: { getter: () => Vencord.Util },
|
||||
|
||||
reload: () => location.reload(),
|
||||
restart: IS_WEB ? DESKTOP_ONLY("restart") : relaunch,
|
||||
|
||||
canonicalizeMatch,
|
||||
canonicalizeReplace,
|
||||
canonicalizeReplacement,
|
||||
runtimeHashMessageKey,
|
||||
|
||||
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
|
||||
fakeRender: (component: ComponentType, props: any) => {
|
||||
const prevWin = fakeRenderWin?.deref();
|
||||
const win = prevWin?.closed === false
|
||||
|
@ -135,8 +158,6 @@ function makeShortcuts() {
|
|||
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
|
||||
},
|
||||
|
||||
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
|
||||
|
||||
channel: { getter: () => getCurrentChannel(), preload: false },
|
||||
channelId: { getter: () => Common.SelectedChannelStore.getChannelId(), preload: false },
|
||||
guild: { getter: () => getCurrentGuild(), preload: false },
|
||||
|
@ -145,6 +166,7 @@ function makeShortcuts() {
|
|||
meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false },
|
||||
messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false },
|
||||
|
||||
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
|
||||
Stores: {
|
||||
getter: () => Object.fromEntries(
|
||||
Common.Flux.Store.getAll()
|
||||
|
@ -159,11 +181,39 @@ function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) {
|
|||
const currentVal = val.getter();
|
||||
if (!currentVal || val.preload === false) return currentVal;
|
||||
|
||||
const value = currentVal[SYM_LAZY_GET]
|
||||
? forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED]
|
||||
: currentVal;
|
||||
function unwrapProxy(value: any) {
|
||||
if (value[SYM_LAZY_GET]) {
|
||||
return forceLoad ? value[SYM_LAZY_GET]() : value[SYM_LAZY_CACHED];
|
||||
} else if (value[SYM_PROXY_INNER_GET]) {
|
||||
return forceLoad ? value[SYM_PROXY_INNER_GET]() : value[SYM_PROXY_INNER_VALUE];
|
||||
} else if (value[SYM_LAZY_COMPONENT_INNER]) {
|
||||
return value[SYM_LAZY_COMPONENT_INNER]() != null ? value[SYM_LAZY_COMPONENT_INNER]() : value;
|
||||
}
|
||||
|
||||
if (value) define(window.shortcutList, key, { value });
|
||||
return value;
|
||||
}
|
||||
|
||||
const value = unwrapProxy(currentVal);
|
||||
if (value != null && typeof value === "object") {
|
||||
const descriptors = Object.getOwnPropertyDescriptors(value);
|
||||
|
||||
for (const propKey in descriptors) {
|
||||
if (value[propKey] == null) continue;
|
||||
|
||||
const descriptor = descriptors[propKey];
|
||||
if (descriptor.writable === true || descriptor.set != null) {
|
||||
const currentValue = value[propKey];
|
||||
const newValue = unwrapProxy(currentValue);
|
||||
if (newValue != null && currentValue !== newValue) {
|
||||
value[propKey] = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
define(window.shortcutList, key, { value });
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -178,8 +228,10 @@ export default definePlugin({
|
|||
const shortcuts = makeShortcuts();
|
||||
window.shortcutList = {};
|
||||
|
||||
for (const [key, val] of Object.entries(shortcuts)) {
|
||||
if ("getter" in val) {
|
||||
for (const key in shortcuts) {
|
||||
const val = shortcuts[key];
|
||||
|
||||
if (Object.hasOwn(val, "getter")) {
|
||||
define(window.shortcutList, key, {
|
||||
get: () => loadAndCacheShortcut(key, val, true)
|
||||
});
|
||||
|
@ -193,8 +245,8 @@ export default definePlugin({
|
|||
}
|
||||
}
|
||||
|
||||
// unproxy loaded modules
|
||||
Webpack.onceReady.then(() => {
|
||||
// Unproxy loaded modules
|
||||
Webpack.onceDiscordLoaded.then(() => {
|
||||
setTimeout(() => this.eagerLoad(false), 1000);
|
||||
|
||||
if (!IS_WEB) {
|
||||
|
@ -205,13 +257,13 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
async eagerLoad(forceLoad: boolean) {
|
||||
await Webpack.onceReady;
|
||||
await Webpack.onceDiscordLoaded;
|
||||
|
||||
const shortcuts = makeShortcuts();
|
||||
for (const key in shortcuts) {
|
||||
const val = shortcuts[key];
|
||||
|
||||
for (const [key, val] of Object.entries(shortcuts)) {
|
||||
if (!Object.hasOwn(val, "getter") || (val as any).preload === false) continue;
|
||||
|
||||
if (!Object.hasOwn(val, "getter") || val.preload === false) continue;
|
||||
try {
|
||||
loadAndCacheShortcut(key, val, forceLoad);
|
||||
} catch { } // swallow not found errors in DEV
|
||||
|
|
|
@ -8,10 +8,10 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { copyWithToast } from "@utils/misc";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findProp } from "@webpack";
|
||||
import { Menu } from "@webpack/common";
|
||||
|
||||
const { convertNameToSurrogate } = findByPropsLazy("convertNameToSurrogate");
|
||||
const convertNameToSurrogate = findProp("convertNameToSurrogate");
|
||||
|
||||
interface Emoji {
|
||||
type: string;
|
||||
|
@ -55,7 +55,7 @@ export default definePlugin({
|
|||
settings,
|
||||
|
||||
contextMenus: {
|
||||
"expression-picker"(children, { target }: { target: Target }) {
|
||||
"expression-picker"(children, { target }: { target: Target; }) {
|
||||
if (target.dataset.type !== "emoji") return;
|
||||
|
||||
children.push(
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# CopyFileContents
|
||||
|
||||
Adds a button to text file attachments to copy their contents.
|
||||
|
||||
![](https://github.com/user-attachments/assets/b1a0f6f4-106f-4953-94d9-4c5ef5810bca)
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./style.css";
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { CopyIcon, NoEntrySignIcon } from "@components/Icons";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { copyWithToast } from "@utils/misc";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Tooltip, useState } from "@webpack/common";
|
||||
|
||||
const CheckMarkIcon = () => {
|
||||
return <svg width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M21.7 5.3a1 1 0 0 1 0 1.4l-12 12a1 1 0 0 1-1.4 0l-6-6a1 1 0 1 1 1.4-1.4L9 16.58l11.3-11.3a1 1 0 0 1 1.4 0Z"></path>
|
||||
</svg>;
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "CopyFileContents",
|
||||
description: "Adds a button to text file attachments to copy their contents",
|
||||
authors: [Devs.Obsidian, Devs.Nuckyz],
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::PREVIEW_BYTES_LEFT}",
|
||||
replacement: {
|
||||
match: /\.footerGap.+?url:\i,fileName:\i,fileSize:\i}\),(?<=fileContents:(\i),bytesLeft:(\i).+?)/g,
|
||||
replace: "$&$self.addCopyButton({fileContents:$1,bytesLeft:$2}),"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
addCopyButton: ErrorBoundary.wrap(({ fileContents, bytesLeft }: { fileContents: string, bytesLeft: number; }) => {
|
||||
const [recentlyCopied, setRecentlyCopied] = useState(false);
|
||||
|
||||
return (
|
||||
<Tooltip text={recentlyCopied ? "Copied!" : bytesLeft > 0 ? "File too large to copy" : "Copy File Contents"}>
|
||||
{tooltipProps => (
|
||||
<div
|
||||
{...tooltipProps}
|
||||
className="vc-cfc-button"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
if (!recentlyCopied && bytesLeft <= 0) {
|
||||
copyWithToast(fileContents);
|
||||
setRecentlyCopied(true);
|
||||
setTimeout(() => setRecentlyCopied(false), 2000);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{recentlyCopied ? <CheckMarkIcon /> : bytesLeft > 0 ? <NoEntrySignIcon color="var(--channel-icon)" /> : <CopyIcon />}
|
||||
</div>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
}, { noop: true }),
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
.vc-cfc-button {
|
||||
color: var(--interactive-normal);
|
||||
cursor: pointer;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.vc-cfc-button:hover {
|
||||
color: var(--interactive-hover);
|
||||
}
|
|
@ -23,22 +23,12 @@ import { Logger } from "@utils/Logger";
|
|||
import { closeAllModals } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { maybePromptToUpdate } from "@utils/updater";
|
||||
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { DraftType, ExpressionPickerStore, FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common";
|
||||
|
||||
const CrashHandlerLogger = new Logger("CrashHandler");
|
||||
|
||||
const { ModalStack, DraftManager } = proxyLazyWebpack(() => {
|
||||
const [ModalStack, DraftManager] = findBulk(
|
||||
filters.byProps("pushLazy", "popAll"),
|
||||
filters.byProps("clearDraft", "saveDraft"),
|
||||
);
|
||||
|
||||
return {
|
||||
ModalStack,
|
||||
DraftManager
|
||||
};
|
||||
});
|
||||
const ModalStack = findByProps("pushLazy", "popAll");
|
||||
const DraftManager = findByProps("clearDraft", "saveDraft");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
attemptToPreventCrashes: {
|
||||
|
@ -67,7 +57,7 @@ export default definePlugin({
|
|||
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::ERRORS_UNEXPECTED_CRASH}",
|
||||
find: ".Messages.ERRORS_UNEXPECTED_CRASH",
|
||||
replacement: {
|
||||
match: /this\.setState\((.+?)\)/,
|
||||
replace: "$self.handleCrash(this,$1);"
|
||||
|
@ -175,7 +165,7 @@ export default definePlugin({
|
|||
}
|
||||
if (settings.store.attemptToNavigateToHome) {
|
||||
try {
|
||||
NavigationRouter.transitionToGuild("@me");
|
||||
NavigationRouter.transitionTo("/channels/@me");
|
||||
} catch (err) {
|
||||
CrashHandlerLogger.debug("Failed to navigate to home", err);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { getUserSettingLazy } from "@api/UserSettings";
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { Link } from "@components/Link";
|
||||
|
@ -26,13 +26,14 @@ import { Margins } from "@utils/margins";
|
|||
import { classes } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { findByCode, findByProps, findComponentByCode } from "@webpack";
|
||||
import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
||||
|
||||
const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color");
|
||||
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
|
||||
const useProfileThemeStyle = findByCode("profileThemeStyle:", "--profile-gradient-primary-color");
|
||||
const ActivityComponent = findComponentByCode("onOpenGameProfile");
|
||||
const ActivityClassName = findByProps("activity", "buttonColor");
|
||||
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
|
||||
|
||||
async function getApplicationAsset(key: string): Promise<string> {
|
||||
if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, "");
|
||||
|
@ -260,7 +261,7 @@ const settings = definePluginSettings({
|
|||
|
||||
function onChange() {
|
||||
setRpc(true);
|
||||
if (Settings.plugins.CustomRPC.enabled) setRpc();
|
||||
if (Vencord.Plugins.isPluginEnabled("CustomRPC")) setRpc();
|
||||
}
|
||||
|
||||
function isStreamLinkDisabled() {
|
||||
|
@ -435,8 +436,8 @@ export default definePlugin({
|
|||
|
||||
<Forms.FormDivider className={Margins.top8} />
|
||||
|
||||
<div style={{ width: "284px", ...profileThemeStyle, padding: 8, marginTop: 8, borderRadius: 8, background: "var(--bg-mod-faint)" }}>
|
||||
{activity[0] && <ActivityComponent activity={activity[0]} channelId={SelectedChannelStore.getChannelId()}
|
||||
<div style={{ width: "284px", ...profileThemeStyle }}>
|
||||
{activity[0] && <ActivityComponent activity={activity[0]} className={ActivityClassName.activity} channelId={SelectedChannelStore.getChannelId()}
|
||||
guild={GuildStore.getGuild(SelectedGuildStore.getLastSelectedGuildId())}
|
||||
application={{ id: settings.store.appID }}
|
||||
user={UserStore.getCurrentUser()} />}
|
||||
|
|
|
@ -37,8 +37,8 @@ export default definePlugin({
|
|||
find: 'type:"IDLE",idle:',
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=Date\.now\(\)-\i>)\i\.\i\|\|/,
|
||||
replace: "$self.getIdleTimeout()||"
|
||||
match: /(?<=Date\.now\(\)-\i>)\i\.\i/,
|
||||
replace: "$self.getIdleTimeout()"
|
||||
},
|
||||
{
|
||||
match: /Math\.min\((\i\.\i\.getSetting\(\)\*\i\.\i\.\i\.SECOND),\i\.\i\)/,
|
||||
|
|
|
@ -46,7 +46,7 @@ const embedUrlRe = /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/;
|
|||
async function embedDidMount(this: Component<Props>) {
|
||||
try {
|
||||
const { embed } = this.props;
|
||||
const { replaceElements, dearrowByDefault } = settings.store;
|
||||
const { replaceElements } = settings.store;
|
||||
|
||||
if (!embed || embed.dearrow || embed.provider?.name !== "YouTube" || !embed.video?.url) return;
|
||||
|
||||
|
@ -63,22 +63,18 @@ async function embedDidMount(this: Component<Props>) {
|
|||
|
||||
if (!hasTitle && !hasThumb) return;
|
||||
|
||||
|
||||
embed.dearrow = {
|
||||
enabled: dearrowByDefault
|
||||
enabled: true
|
||||
};
|
||||
|
||||
if (hasTitle && replaceElements !== ReplaceElements.ReplaceThumbnailsOnly) {
|
||||
const replacementTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2");
|
||||
|
||||
embed.dearrow.oldTitle = dearrowByDefault ? embed.rawTitle : replacementTitle;
|
||||
if (dearrowByDefault) embed.rawTitle = replacementTitle;
|
||||
embed.dearrow.oldTitle = embed.rawTitle;
|
||||
embed.rawTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2");
|
||||
}
|
||||
if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) {
|
||||
const replacementProxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
||||
|
||||
embed.dearrow.oldThumb = dearrowByDefault ? embed.thumbnail.proxyURL : replacementProxyURL;
|
||||
if (dearrowByDefault) embed.thumbnail.proxyURL = replacementProxyURL;
|
||||
if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) {
|
||||
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
|
||||
embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
||||
}
|
||||
|
||||
this.forceUpdate();
|
||||
|
@ -100,7 +96,6 @@ function DearrowButton({ component }: { component: Component<Props>; }) {
|
|||
className={"vc-dearrow-toggle-" + (embed.dearrow.enabled ? "on" : "off")}
|
||||
onClick={() => {
|
||||
const { enabled, oldThumb, oldTitle } = embed.dearrow;
|
||||
settings.store.dearrowByDefault = !enabled;
|
||||
embed.dearrow.enabled = !enabled;
|
||||
if (oldTitle) {
|
||||
embed.dearrow.oldTitle = embed.rawTitle;
|
||||
|
@ -158,12 +153,6 @@ const settings = definePluginSettings({
|
|||
{ label: "Titles", value: ReplaceElements.ReplaceTitlesOnly },
|
||||
{ label: "Thumbnails", value: ReplaceElements.ReplaceThumbnailsOnly },
|
||||
],
|
||||
},
|
||||
dearrowByDefault: {
|
||||
description: "Dearrow videos automatically",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
restartNeeded: false
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "DefaultCustomizationSections",
|
||||
replacement: {
|
||||
match: /(?<=#{intl::USER_SETTINGS_AVATAR_DECORATION}\)},"decoration"\),)/,
|
||||
match: /(?<=USER_SETTINGS_AVATAR_DECORATION},"decoration"\),)/,
|
||||
replace: "$self.DecorSection(),"
|
||||
}
|
||||
},
|
||||
|
@ -54,7 +54,7 @@ export default definePlugin({
|
|||
replace: "$self.DecorationGridItem=$&"
|
||||
},
|
||||
{
|
||||
match: /(?<==)\i=>{let{user:\i,avatarDecoration/,
|
||||
match: /(?<==)\i=>{let{user:\i,avatarDecoration.{300,600}decorationGridItemChurned/,
|
||||
replace: "$self.DecorationGridDecoration=$&"
|
||||
},
|
||||
// Remove NEW label from decor avatar decorations
|
||||
|
|
|
@ -93,7 +93,7 @@ export const useAuthorizationStore = proxyLazy(() => zustandCreate(
|
|||
} as AuthorizationState),
|
||||
{
|
||||
name: "decor-auth",
|
||||
storage: indexedDBStorage,
|
||||
getStorage: () => indexedDBStorage,
|
||||
partialize: state => ({ tokens: state.tokens }),
|
||||
onRehydrateStorage: () => state => state?.init()
|
||||
}
|
||||
|
|
|
@ -95,39 +95,24 @@ export const useUsersDecorationsStore = proxyLazy(() => zustandCreate((set: any,
|
|||
} as UsersDecorationsState)));
|
||||
|
||||
export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | null | undefined {
|
||||
try {
|
||||
const [decorAvatarDecoration, setDecorAvatarDecoration] = useState<string | null>(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null);
|
||||
const [decorAvatarDecoration, setDecorAvatarDecoration] = useState<string | null>(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null);
|
||||
|
||||
useEffect(() => {
|
||||
const destructor = (() => {
|
||||
try {
|
||||
return useUsersDecorationsStore.subscribe(
|
||||
state => {
|
||||
if (!user) return;
|
||||
const newDecorAvatarDecoration = state.getAsset(user.id);
|
||||
if (!newDecorAvatarDecoration) return;
|
||||
if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration);
|
||||
}
|
||||
);
|
||||
} catch {
|
||||
return () => { };
|
||||
}
|
||||
})();
|
||||
useEffect(() => {
|
||||
const destructor = useUsersDecorationsStore.subscribe(
|
||||
state => {
|
||||
if (!user) return;
|
||||
const newDecorAvatarDecoration = state.getAsset(user.id);
|
||||
if (!newDecorAvatarDecoration) return;
|
||||
if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration);
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
if (user) {
|
||||
const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState();
|
||||
fetchUserDecorAvatarDecoration(user.id);
|
||||
}
|
||||
} catch { }
|
||||
if (user) {
|
||||
const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState();
|
||||
fetchUserDecorAvatarDecoration(user.id);
|
||||
}
|
||||
return destructor;
|
||||
}, []);
|
||||
|
||||
return destructor;
|
||||
}, []);
|
||||
|
||||
return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { Flex } from "@components/Flex";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
import { Button, useEffect } from "@webpack/common";
|
||||
|
||||
import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore";
|
||||
|
@ -13,7 +13,7 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco
|
|||
import { cl } from "../";
|
||||
import { openChangeDecorationModal } from "../modals/ChangeDecorationModal";
|
||||
|
||||
const CustomizationSection = findByCodeLazy(".customizationSectionBackground");
|
||||
const CustomizationSection = findComponentByCode(".customizationSectionBackground");
|
||||
|
||||
export interface DecorSectionProps {
|
||||
hideTitle?: boolean;
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
*/
|
||||
|
||||
import { PlusIcon } from "@components/Icons";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { Text } from "@webpack/common";
|
||||
import { i18n, Text } from "@webpack/common";
|
||||
import { HTMLProps } from "react";
|
||||
|
||||
import { DecorationGridItem } from ".";
|
||||
|
@ -25,7 +24,7 @@ export default function DecorationGridCreate(props: DecorationGridCreateProps) {
|
|||
variant="text-xs/normal"
|
||||
color="header-primary"
|
||||
>
|
||||
{getIntlMessage("CREATE")}
|
||||
{i18n.Messages.CREATE}
|
||||
</Text>
|
||||
</DecorationGridItem >;
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
*/
|
||||
|
||||
import { NoEntrySignIcon } from "@components/Icons";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { Text } from "@webpack/common";
|
||||
import { i18n, Text } from "@webpack/common";
|
||||
import { HTMLProps } from "react";
|
||||
|
||||
import { DecorationGridItem } from ".";
|
||||
|
@ -25,7 +24,7 @@ export default function DecorationGridNone(props: DecorationGridNoneProps) {
|
|||
variant="text-xs/normal"
|
||||
color="header-primary"
|
||||
>
|
||||
{getIntlMessage("NONE")}
|
||||
{i18n.Messages.NONE}
|
||||
</Text>
|
||||
</DecorationGridItem >;
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { React } from "@webpack/common";
|
||||
|
||||
import { cl } from "../";
|
||||
import Grid, { GridProps } from "./Grid";
|
||||
|
||||
const ScrollerClasses = findByPropsLazy("managedReactiveScroller");
|
||||
const ScrollerClasses = findByProps("managedReactiveScroller");
|
||||
|
||||
type Section<SectionT, ItemT> = SectionT & {
|
||||
items: Array<ItemT>;
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findComponentByCode, LazyComponentWebpack } from "@webpack";
|
||||
import { NoopComponent } from "@utils/react";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
import { React } from "@webpack/common";
|
||||
import type { ComponentType, HTMLProps, PropsWithChildren } from "react";
|
||||
|
||||
|
@ -15,13 +16,10 @@ type DecorationGridItemComponent = ComponentType<PropsWithChildren<HTMLProps<HTM
|
|||
isSelected: boolean,
|
||||
}>;
|
||||
|
||||
export let DecorationGridItem: DecorationGridItemComponent;
|
||||
export let DecorationGridItem: DecorationGridItemComponent = NoopComponent;
|
||||
export const setDecorationGridItem = v => DecorationGridItem = v;
|
||||
|
||||
export const AvatarDecorationModalPreview = LazyComponentWebpack(() => {
|
||||
const component = findComponentByCode(".shopPreviewBanner");
|
||||
return React.memo(component);
|
||||
});
|
||||
export const AvatarDecorationModalPreview = findComponentByCode<any>(".shopPreviewBanner", component => React.memo(component));
|
||||
|
||||
type DecorationGridDecorationComponent = React.ComponentType<HTMLProps<HTMLDivElement> & {
|
||||
avatarDecoration: AvatarDecoration;
|
||||
|
@ -29,5 +27,5 @@ type DecorationGridDecorationComponent = React.ComponentType<HTMLProps<HTMLDivEl
|
|||
isSelected: boolean,
|
||||
}>;
|
||||
|
||||
export let DecorationGridDecoration: DecorationGridDecorationComponent;
|
||||
export let DecorationGridDecoration: DecorationGridDecorationComponent = NoopComponent;
|
||||
export const setDecorationGridDecoration = v => DecorationGridDecoration = v;
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { extractAndLoadChunksLazy, findByPropsLazy } from "@webpack";
|
||||
import { extractAndLoadChunksLazy, findByProps } from "@webpack";
|
||||
|
||||
export const cl = classNameFactory("vc-decor-");
|
||||
export const DecorationModalStyles = findByPropsLazy("modalFooterShopButton");
|
||||
export const DecorationModalStyles = findByProps("modalFooterShopButton");
|
||||
|
||||
export const requireAvatarDecorationModal = extractAndLoadChunksLazy([".COLLECTIBLES_SHOP_FULLSCREEN&&"]);
|
||||
export const requireCreateStickerModal = extractAndLoadChunksLazy(["stickerInspected]:"]);
|
||||
export const requireAvatarDecorationModal = extractAndLoadChunksLazy(".COLLECTIBLES_SHOP_FULLSCREEN&&");
|
||||
export const requireCreateStickerModal = extractAndLoadChunksLazy("stickerInspected]:");
|
||||
|
|
|
@ -10,7 +10,7 @@ import { openInviteModal } from "@utils/discord";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { classes, copyWithToast } from "@utils/misc";
|
||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
|
@ -29,7 +29,7 @@ import SectionedGridList from "../components/SectionedGridList";
|
|||
import { openCreateDecorationModal } from "./CreateDecorationModal";
|
||||
import { openGuidelinesModal } from "./GuidelinesModal";
|
||||
|
||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
|
||||
function usePresets() {
|
||||
const [presets, setPresets] = useState<Preset[]>([]);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Link } from "@components/Link";
|
|||
import { openInviteModal } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { filters, findComponentByCodeLazy, mapMangledModuleLazy } from "@webpack";
|
||||
import { filters, findComponentByCode, mapMangledModule } from "@webpack";
|
||||
import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Text, TextInput, useEffect, useMemo, UserStore, useState } from "@webpack/common";
|
||||
|
||||
import { GUILD_ID, INVITE_KEY, RAW_SKU_ID } from "../../lib/constants";
|
||||
|
@ -17,11 +17,11 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco
|
|||
import { cl, DecorationModalStyles, requireAvatarDecorationModal, requireCreateStickerModal } from "../";
|
||||
import { AvatarDecorationModalPreview } from "../components";
|
||||
|
||||
const FileUpload = findComponentByCodeLazy("fileUploadInput,");
|
||||
const FileUpload = findComponentByCode("fileUploadInput,");
|
||||
|
||||
const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE=3]="POSITIVE', {
|
||||
const { HelpMessage, HelpMessageTypes } = mapMangledModule('POSITIVE=3]="POSITIVE', {
|
||||
HelpMessage: filters.componentByCode(".iconDiv,", "messageType"),
|
||||
HelpMessageTypes: filters.byProps("POSITIVE", "WARNING"),
|
||||
HelpMessage: filters.byCode(".iconDiv")
|
||||
});
|
||||
|
||||
function useObjectURL(object: Blob | MediaSource | null) {
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Devs } from "@utils/constants";
|
|||
import { Logger } from "@utils/Logger";
|
||||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
||||
import { filters, findAll, search } from "@webpack";
|
||||
import { cacheFindAll, filters, searchFactories } from "@webpack";
|
||||
|
||||
const PORT = 8485;
|
||||
const NAV_ID = "dev-companion-reconnect";
|
||||
|
@ -154,13 +154,13 @@ function initWs(isManual = false) {
|
|||
case "testPatch": {
|
||||
const { find, replacement } = data as PatchData;
|
||||
|
||||
const candidates = search(find);
|
||||
const candidates = searchFactories(find);
|
||||
const keys = Object.keys(candidates);
|
||||
if (keys.length !== 1)
|
||||
return reply("Expected exactly one 'find' matches, found " + keys.length);
|
||||
|
||||
const mod = candidates[keys[0]];
|
||||
let src = String(mod.original ?? mod).replaceAll("\n", "");
|
||||
let src = String(mod).replaceAll("\n", "");
|
||||
|
||||
if (src.startsWith("function(")) {
|
||||
src = "0," + src;
|
||||
|
@ -201,22 +201,22 @@ function initWs(isManual = false) {
|
|||
let results: any[];
|
||||
switch (type.replace("find", "").replace("Lazy", "")) {
|
||||
case "":
|
||||
results = findAll(parsedArgs[0]);
|
||||
results = cacheFindAll(parsedArgs[0]);
|
||||
break;
|
||||
case "ByProps":
|
||||
results = findAll(filters.byProps(...parsedArgs));
|
||||
results = cacheFindAll(filters.byProps(...parsedArgs));
|
||||
break;
|
||||
case "Store":
|
||||
results = findAll(filters.byStoreName(parsedArgs[0]));
|
||||
results = cacheFindAll(filters.byStoreName(parsedArgs[0]));
|
||||
break;
|
||||
case "ByCode":
|
||||
results = findAll(filters.byCode(...parsedArgs));
|
||||
results = cacheFindAll(filters.byCode(...parsedArgs));
|
||||
break;
|
||||
case "ModuleId":
|
||||
results = Object.keys(search(parsedArgs[0]));
|
||||
results = Object.keys(searchFactories(parsedArgs[0]));
|
||||
break;
|
||||
case "ComponentByCode":
|
||||
results = findAll(filters.componentByCode(...parsedArgs));
|
||||
results = cacheFindAll(filters.componentByCode(...parsedArgs));
|
||||
break;
|
||||
default:
|
||||
return reply("Unknown Find Type " + type);
|
||||
|
|
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||
authors: [Devs.Nuckyz],
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::BOT_CALL_IDLE_DISCONNECT_2}",
|
||||
find: ".Messages.BOT_CALL_IDLE_DISCONNECT",
|
||||
replacement: {
|
||||
match: /,?(?=\i\(this,"idleTimeout",new \i\.\i\))/,
|
||||
replace: ";return;"
|
||||
|
|
|
@ -23,12 +23,12 @@ import { Logger } from "@utils/Logger";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { findByCode, findStore } from "@webpack";
|
||||
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
||||
import { Promisable } from "type-fest";
|
||||
|
||||
const StickersStore = findStoreLazy("StickersStore");
|
||||
const uploadEmoji = findByCodeLazy(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START");
|
||||
const StickersStore = findStore("StickersStore");
|
||||
const uploadEmoji = findByCode(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START");
|
||||
|
||||
interface Sticker {
|
||||
t: "Sticker";
|
||||
|
|
|
@ -23,13 +23,12 @@ import { ErrorCard } from "@components/ErrorCard";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Margins } from "@utils/margins";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Forms, React } from "@webpack/common";
|
||||
|
||||
import hideBugReport from "./hideBugReport.css?managed";
|
||||
|
||||
const KbdStyles = findByPropsLazy("key", "combo");
|
||||
const BugReporterExperiment = findLazy(m => m?.definition?.id === "2024-09_bug_reporter");
|
||||
const KbdStyles = findByProps("key", "combo");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
toolbarDevMenu: {
|
||||
|
@ -79,8 +78,8 @@ export default definePlugin({
|
|||
{
|
||||
find: "toolbar:function",
|
||||
replacement: {
|
||||
match: /hasBugReporterAccess:(\i)/,
|
||||
replace: "_hasBugReporterAccess:$1=true"
|
||||
match: /\i\.isStaff\(\)/,
|
||||
replace: "true"
|
||||
},
|
||||
predicate: () => settings.store.toolbarDevMenu
|
||||
},
|
||||
|
@ -92,18 +91,10 @@ export default definePlugin({
|
|||
match: /\i\.isDM\(\)\|\|\i\.isThread\(\)/,
|
||||
replace: "false",
|
||||
}
|
||||
},
|
||||
// enable option to always record clips even if you are not streaming
|
||||
{
|
||||
find: "isDecoupledGameClippingEnabled(){",
|
||||
replacement: {
|
||||
match: /\i\.isStaff\(\)/,
|
||||
replace: "true"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
start: () => !BugReporterExperiment.getCurrentConfig().hasBugReporterAccess && enableStyle(hideBugReport),
|
||||
start: () => enableStyle(hideBugReport),
|
||||
stop: () => disableStyle(hideBugReport),
|
||||
|
||||
settingsAboutComponent: () => {
|
||||
|
|
|
@ -20,39 +20,39 @@ import { addPreEditListener, addPreSendListener, removePreEditListener, removePr
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
|
||||
import { getCurrentGuild, getEmojiURL } from "@utils/discord";
|
||||
import { getCurrentGuild } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType, Patch } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
|
||||
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCode, findByProps, findStore, webpackDependantLazy } from "@webpack";
|
||||
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||
import type { Emoji } from "@webpack/types";
|
||||
import type { Message } from "discord-types/general";
|
||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
|
||||
const StickerStore = findStoreLazy("StickersStore") as {
|
||||
const StickersStore = findStore("StickersStore") as {
|
||||
getPremiumPacks(): StickerPack[];
|
||||
getAllGuildStickers(): Map<string, Sticker[]>;
|
||||
getStickerById(id: string): Sticker | undefined;
|
||||
};
|
||||
|
||||
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
||||
const UserSettingsProtoStore = findStore("UserSettingsProtoStore");
|
||||
|
||||
const BINARY_READ_OPTIONS = findByPropsLazy("readerFactory");
|
||||
const BINARY_READ_OPTIONS = findByProps("readerFactory");
|
||||
|
||||
function searchProtoClassField(localName: string, protoClass: any) {
|
||||
const field = protoClass?.fields?.find((field: any) => field.localName === localName);
|
||||
if (!field) return;
|
||||
|
||||
const fieldGetter = Object.values(field).find(value => typeof value === "function") as any;
|
||||
const fieldGetter = Object.values<any>(field).find(value => typeof value === "function");
|
||||
return fieldGetter?.();
|
||||
}
|
||||
|
||||
const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
||||
const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
||||
const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
||||
const PreloadedUserSettingsActionCreators = webpackDependantLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
||||
const AppearanceSettingsActionCreators = webpackDependantLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
||||
const ClientThemeSettingsActionsCreators = webpackDependantLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
||||
|
||||
const isUnusableRoleSubscriptionEmoji = findByCodeLazy(".getUserIsAdmin(");
|
||||
const isUnusableRoleSubscriptionEmoji = findByCode(".getUserIsAdmin(");
|
||||
|
||||
const enum EmojiIntentions {
|
||||
REACTION,
|
||||
|
@ -194,26 +194,6 @@ const hasExternalStickerPerms = (channelId: string) => hasPermission(channelId,
|
|||
const hasEmbedPerms = (channelId: string) => hasPermission(channelId, PermissionsBits.EMBED_LINKS);
|
||||
const hasAttachmentPerms = (channelId: string) => hasPermission(channelId, PermissionsBits.ATTACH_FILES);
|
||||
|
||||
function makeBypassPatches(): Omit<Patch, "plugin"> {
|
||||
const mapping: Array<{ func: string, predicate?: () => boolean; }> = [
|
||||
{ func: "canUseCustomStickersEverywhere", predicate: () => settings.store.enableStickerBypass },
|
||||
{ func: "canUseHighVideoUploadQuality", predicate: () => settings.store.enableStreamQualityBypass },
|
||||
{ func: "canStreamQuality", predicate: () => settings.store.enableStreamQualityBypass },
|
||||
{ func: "canUseClientThemes" },
|
||||
{ func: "canUseCustomNotificationSounds" },
|
||||
{ func: "canUsePremiumAppIcons" }
|
||||
];
|
||||
|
||||
return {
|
||||
find: "canUseCustomStickersEverywhere:",
|
||||
replacement: mapping.map(({ func, predicate }) => ({
|
||||
match: new RegExp(String.raw`(?<=${func}:function\(\i(?:,\i)?\){)`),
|
||||
replace: "return true;",
|
||||
predicate
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "FakeNitro",
|
||||
authors: [Devs.Arjix, Devs.D3SOX, Devs.Ven, Devs.fawn, Devs.captain, Devs.Nuckyz, Devs.AutumnVN],
|
||||
|
@ -223,17 +203,6 @@ export default definePlugin({
|
|||
settings,
|
||||
|
||||
patches: [
|
||||
// General bypass patches
|
||||
makeBypassPatches(),
|
||||
// Patch the emoji picker in voice calls to not be bypassed by fake nitro
|
||||
{
|
||||
find: "emojiItemDisabled]",
|
||||
predicate: () => settings.store.enableEmojiBypass,
|
||||
replacement: {
|
||||
match: /CHAT/,
|
||||
replace: "STATUS"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: ".PREMIUM_LOCKED;",
|
||||
group: true,
|
||||
|
@ -274,6 +243,15 @@ export default definePlugin({
|
|||
replace: (_, rest1, rest2) => `${rest1},fakeNitroOriginal){if(!fakeNitroOriginal)return false;${rest2}`
|
||||
}
|
||||
},
|
||||
// Allow stickers to be sent everywhere
|
||||
{
|
||||
find: "canUseCustomStickersEverywhere:function",
|
||||
predicate: () => settings.store.enableStickerBypass,
|
||||
replacement: {
|
||||
match: /canUseCustomStickersEverywhere:function\(\i\){/,
|
||||
replace: "$&return true;"
|
||||
},
|
||||
},
|
||||
// Make stickers always available
|
||||
{
|
||||
find: '"SENDABLE"',
|
||||
|
@ -283,15 +261,37 @@ export default definePlugin({
|
|||
replace: "true?"
|
||||
}
|
||||
},
|
||||
// Allow streaming with high quality
|
||||
{
|
||||
find: "canUseHighVideoUploadQuality:function",
|
||||
predicate: () => settings.store.enableStreamQualityBypass,
|
||||
replacement: [
|
||||
"canUseHighVideoUploadQuality",
|
||||
"canStreamQuality",
|
||||
].map(func => {
|
||||
return {
|
||||
match: new RegExp(`${func}:function\\(\\i(?:,\\i)?\\){`, "g"),
|
||||
replace: "$&return true;"
|
||||
};
|
||||
})
|
||||
},
|
||||
// Remove boost requirements to stream with high quality
|
||||
{
|
||||
find: "#{intl::STREAM_FPS_OPTION}",
|
||||
find: "STREAM_FPS_OPTION.format",
|
||||
predicate: () => settings.store.enableStreamQualityBypass,
|
||||
replacement: {
|
||||
match: /guildPremiumTier:\i\.\i\.TIER_\d,?/g,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
// Allow client themes to be changeable
|
||||
{
|
||||
find: "canUseClientThemes:function",
|
||||
replacement: {
|
||||
match: /canUseClientThemes:function\(\i\){/,
|
||||
replace: "$&return true;"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: '"UserSettingsProtoStore"',
|
||||
replacement: [
|
||||
|
@ -350,13 +350,13 @@ export default definePlugin({
|
|||
{
|
||||
// Filter attachments to remove fake nitro stickers or emojis
|
||||
predicate: () => settings.store.transformStickers,
|
||||
match: /renderAttachments\(\i\){.+?{attachments:(\i).+?;/,
|
||||
match: /renderAttachments\(\i\){let{attachments:(\i).+?;/,
|
||||
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: "#{intl::STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION}",
|
||||
find: ".Messages.STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION.format",
|
||||
predicate: () => settings.store.transformStickers,
|
||||
replacement: [
|
||||
{
|
||||
|
@ -381,7 +381,7 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: "#{intl::EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION}",
|
||||
find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION",
|
||||
predicate: () => settings.store.transformEmojis,
|
||||
replacement: {
|
||||
// Add the fake nitro emoji notice
|
||||
|
@ -389,6 +389,14 @@ export default definePlugin({
|
|||
replace: (_, reactNode, props) => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!${props}?.fakeNitroNode?.fake)`
|
||||
}
|
||||
},
|
||||
// Allow using custom app icons
|
||||
{
|
||||
find: "canUsePremiumAppIcons:function",
|
||||
replacement: {
|
||||
match: /canUsePremiumAppIcons:function\(\i\){/,
|
||||
replace: "$&return true;"
|
||||
}
|
||||
},
|
||||
// Separate patch for allowing using custom app icons
|
||||
{
|
||||
find: /\.getCurrentDesktopIcon.{0,25}\.isPremium/,
|
||||
|
@ -404,6 +412,14 @@ export default definePlugin({
|
|||
match: /(?<=type:"(?:SOUNDBOARD_SOUNDS_RECEIVED|GUILD_SOUNDBOARD_SOUND_CREATE|GUILD_SOUNDBOARD_SOUND_UPDATE|GUILD_SOUNDBOARD_SOUNDS_UPDATE)".+?available:)\i\.available/g,
|
||||
replace: "true"
|
||||
}
|
||||
},
|
||||
// Allow using custom notification sounds
|
||||
{
|
||||
find: "canUseCustomNotificationSounds:function",
|
||||
replacement: {
|
||||
match: /canUseCustomNotificationSounds:function\(\i\){/,
|
||||
replace: "$&return true;"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -550,8 +566,8 @@ export default definePlugin({
|
|||
|
||||
const gifMatch = child.props.href.match(fakeNitroGifStickerRegex);
|
||||
if (gifMatch) {
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||
if (StickerStore.getStickerById(gifMatch[1])) return null;
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
|
||||
if (StickersStore.getStickerById(gifMatch[1])) return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,7 +655,7 @@ export default definePlugin({
|
|||
url = new URL(item);
|
||||
} catch { }
|
||||
|
||||
const stickerName = StickerStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker";
|
||||
const stickerName = StickersStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker";
|
||||
stickers.push({
|
||||
format_type: 1,
|
||||
id: imgMatch[1],
|
||||
|
@ -652,9 +668,9 @@ export default definePlugin({
|
|||
|
||||
const gifMatch = item.match(fakeNitroGifStickerRegex);
|
||||
if (gifMatch) {
|
||||
if (!StickerStore.getStickerById(gifMatch[1])) continue;
|
||||
if (!StickersStore.getStickerById(gifMatch[1])) continue;
|
||||
|
||||
const stickerName = StickerStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker";
|
||||
const stickerName = StickersStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker";
|
||||
stickers.push({
|
||||
format_type: 2,
|
||||
id: gifMatch[1],
|
||||
|
@ -687,8 +703,8 @@ export default definePlugin({
|
|||
|
||||
const gifMatch = embed.url!.match(fakeNitroGifStickerRegex);
|
||||
if (gifMatch) {
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||
if (StickerStore.getStickerById(gifMatch[1])) return true;
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
|
||||
if (StickersStore.getStickerById(gifMatch[1])) return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -705,8 +721,8 @@ export default definePlugin({
|
|||
|
||||
const match = attachment.url.match(fakeNitroGifStickerRegex);
|
||||
if (match) {
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||
if (StickerStore.getStickerById(match[1])) return false;
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
|
||||
if (StickersStore.getStickerById(match[1])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -862,7 +878,7 @@ export default definePlugin({
|
|||
if (!s.enableStickerBypass)
|
||||
break stickerBypass;
|
||||
|
||||
const sticker = StickerStore.getStickerById(extra.stickers?.[0]!);
|
||||
const sticker = StickersStore.getStickerById(extra.stickers?.[0]!);
|
||||
if (!sticker)
|
||||
break stickerBypass;
|
||||
|
||||
|
@ -920,7 +936,7 @@ export default definePlugin({
|
|||
|
||||
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
|
||||
|
||||
const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize));
|
||||
const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize }));
|
||||
url.searchParams.set("size", s.emojiSize.toString());
|
||||
url.searchParams.set("name", emoji.name);
|
||||
|
||||
|
@ -953,7 +969,7 @@ export default definePlugin({
|
|||
|
||||
hasBypass = true;
|
||||
|
||||
const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize));
|
||||
const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize }));
|
||||
url.searchParams.set("size", s.emojiSize.toString());
|
||||
url.searchParams.set("name", emoji.name);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue