Compare commits

...

1183 Commits

Author SHA1 Message Date
V
ea11f2244f
README: Add sponsors 2023-11-13 01:37:15 +01:00
V
96126fa39f
remove pipebomb from github actions (#1968) 2023-11-09 07:15:49 +01:00
AM
9bd82943e3
[Plugin] Super Reaction Tweaks (#1958)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Jack Matthews <jm5112356@gmail.com>
2023-11-09 04:42:35 +01:00
V
5edc94062c
make packageManager key less specific 2023-11-09 02:42:34 +01:00
AutumnVN
394d2060eb
searchReply: fix (#1961) 2023-11-09 02:34:40 +01:00
V
119b628f33
feat: simple plugin natives (#1965) 2023-11-09 02:32:34 +01:00
zImPatrick
32f2043193
Fix FakeNitro sticker bypass (#1964) 2023-11-07 22:24:17 -03:00
Marvin Witt
04d2dd26c4
fix(dearrow): don't replace thumbnail if only original available (#1959) 2023-11-07 22:24:08 -03:00
Vendicated
86e94343cc
bump to v1.6.3 2023-11-05 02:07:33 +01:00
Vendicated
dd44ac1ad2
OpenInApp: Support podcasts 2023-11-04 19:08:44 +01:00
Vendicated
370b3d366d
OpenInApp: Fix links in messages 2023-11-04 18:59:16 +01:00
Vendicated
a67c7f841d
Fix ViewIcons correctly 2023-11-04 18:54:29 +01:00
Vendicated
2c9793202d
Revert "Fix ViewIcons"
This reverts commit 098da8c3fd95cdd1f299b86d0b919b001009a1df.
2023-11-04 18:54:02 +01:00
AutumnVN
a257926609
customRpc: fix discord attachment link (#1949) 2023-11-04 18:45:17 +01:00
AutumnVN
77659be4f0
fakeNitro: disallow emoji in add reaction (#1954) 2023-11-04 18:44:53 +01:00
AutumnVN
37b9a62460
favGifSearch: fix search bar (#1955) 2023-11-04 18:44:29 +01:00
Lewis Crichton
7f73e13364
docs: point people to advisories for security bugs (#1957) 2023-11-04 18:41:38 +01:00
Vendicated
fcf2bdda70
fix TypingTweaks 2023-11-03 02:03:58 +01:00
AutumnVN
fa9da2d693
noMosaic: play video inline + optional media layout type (#1946) 2023-11-03 01:57:39 +01:00
AutumnVN
44b21394b3
gifPaste: fix unable to use gif picker in profile customization (#1947) 2023-11-03 01:56:31 +01:00
Nuckyz
27fffc8bc3
Fix ShowMeYourName 2023-11-01 23:28:52 -03:00
Nuckyz
098da8c3fd
Fix ViewIcons 2023-11-01 23:26:13 -03:00
Nuckyz
9cf88d4232
Fix MessageDecorationsAPI 2023-11-01 22:46:06 -03:00
Vendicated
dd61b0c999
bump to v1.6.2 2023-11-01 02:25:13 +01:00
Haruka
5dc0d06be1
EmoteCloner: make the error toasts useful (#1938)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-11-01 02:23:45 +01:00
AutumnVN
9de6c2d4ff
previewMessage: fix button (#1919) 2023-11-01 02:19:26 +01:00
AutumnVN
e37f62ac0a
imageZoom: dont close carousel modal on image click (#1926)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-11-01 02:19:02 +01:00
AutumnVN
56a9d79f85
PiP: fix issues / styles (#1927) 2023-11-01 02:14:32 +01:00
Nuckyz
9d78233afa
Fix displaying BetterFolders sidebar when watching streams in fullscreen 2023-11-01 02:10:58 +01:00
Vendicated
9af2ec65ae
OnePingPerDM: fix server pings 2023-11-01 02:09:43 +01:00
Vendicated
584885acf5
[skip ci] Revert "add react linting"
doesnt work properly :(

This reverts commit 18fdc33ee7d1f60d58645c2a98f402988b97e996.
2023-10-31 23:56:13 +01:00
Vendicated
18fdc33ee7
[skip ci] add react linting 2023-10-31 23:50:55 +01:00
V
522fdcd15d
WebKeyBinds: Fix & make available on ArmCord 2023-10-28 23:51:04 +02:00
Nuckyz
af135b9245
Fix AlwaysAnimate 2023-10-28 17:43:27 -03:00
Nuckyz
4b958d17fd
Fix BetterFolders freeze and add new options (#1923) 2023-10-28 20:18:00 +00:00
Nuckyz
bfb48b4faf
BetterFolders: Option to choose whether to keep guild icons (#1918) 2023-10-28 04:00:17 +00:00
Hugo C
9dd8e72245
fix: PiP replacing video download button (#1910) 2023-10-28 02:22:04 +02:00
AutumnVN
aae790f1c1
vencordToolbox: correct hover color + oneko (#1913) 2023-10-28 02:21:35 +02:00
AutumnVN
7f17e70697
noSystemBadge: fix (#1914) 2023-10-28 02:20:29 +02:00
Vendicated
b3311c6f12
BetterGifAltText: Fix displaying undefined 2023-10-28 02:19:43 +02:00
Nuckyz
bc09225258
Add missing patches predicates to BetterFolders 2023-10-27 20:10:44 -03:00
Nuckyz
9ce923d4d7
Fix BetterFolders 2023-10-27 19:56:11 -03:00
Nuckyz
7845af0802
Remove hacks to support no longer active versions of Discord 2023-10-27 19:55:39 -03:00
Nuckyz
89672882b9
PermViewer: Fix incorrectly displaying some role names as Unknown Role 2023-10-27 14:33:18 -03:00
Nuckyz
e05c630a54
SHC: Make Chat Input Bar channel list include hidden channels 2023-10-27 14:29:25 -03:00
Nuckyz
38834ef7ac
Fix git updater 2023-10-27 13:03:52 -03:00
Luna
98d49af728
Fix git updater for other branches (#1915) 2023-10-27 12:09:39 -03:00
Susheel Thapa
0afe319141
fix typo in multiple files (#1911) 2023-10-27 12:09:38 -03:00
Vendicated
a9e67e2955
Bump to v1.6.1 2023-10-27 03:53:58 +02:00
Nuckyz
1676956f61
Fix hidden channels triggering the unread box 2023-10-26 20:59:48 -03:00
Nuckyz
8692109bc5
Add comments to some SHC patches 2023-10-26 18:13:25 -03:00
Nuckyz
0847f205b8
Fix some hidden channels not collapsing 2023-10-26 18:11:00 -03:00
AutumnVN
2f94e167c4
noProfileThemes: fix usrbg compatibility (#1905) 2023-10-26 22:52:48 +02:00
Vendicated
589c070773
fix not removing vencord 'chunks' from webpack 2023-10-26 22:51:07 +02:00
Nuckyz
8567ff6239
Little bit of SHC cleanup 2023-10-26 16:51:37 -03:00
Vendicated
e4701769a5
Fix entry webpack modules not being patched 2023-10-26 21:21:21 +02:00
Vendicated
25f101602d
fix modules being patched multiple times 2023-10-26 21:03:05 +02:00
Nuckyz
85bfa1e719
Fix duplicated WebContextMenus find 2023-10-26 15:36:05 -03:00
Nuckyz
b48998d485
More accurate ShowAllMessageButtons patch 2023-10-26 15:25:29 -03:00
Nuckyz
6d605050e1
Fix BetterNoteBox 2023-10-26 15:07:44 -03:00
TheKodeToad
07c4a097e0
Fix EmoteCloner (#1907) 2023-10-26 13:49:18 -03:00
TheKodeToad
c1de41436a
Fix plugins using promptToUpload (#1908) 2023-10-26 13:49:06 -03:00
Nuckyz
64c6f5740f
Fix FakeNitro completely (#1903) 2023-10-26 03:19:26 +00:00
AutumnVN
03523446c1
silentTyping: fix showIcon toggle (#1898) 2023-10-26 00:50:33 +00:00
Vendicated
25dc25c707
Fix VoiceMessages 2023-10-26 02:28:17 +02:00
Hugo C
8ac8048845
fix: ImageZoom + PiP (#1894)
Co-authored-by: V <vendicated@riseup.net>
2023-10-26 02:17:31 +02:00
Vendicated
ffe6bb1c5d
Bump to v1.6.0 2023-10-26 01:32:15 +02:00
Vendicated
74faaa216f
Fix PlatformIndicators colorMobileIndicator 2023-10-26 01:29:52 +02:00
Luna
a6b8b59d5c
fix: shikicodeblocks, betterroledot (#1895) 2023-10-26 01:03:11 +02:00
redstonekasi
9eaeb24196
fix: RoleColorEverywhere (#1884)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-10-26 00:56:39 +02:00
Nuckyz
6b28e5ad85
Partially fix FakeNitro 2023-10-25 19:38:35 -03:00
Vendicated
d852393cfb
fix UserVoiceShow 2023-10-26 00:36:28 +02:00
IThundxr
0c12500c0a
Fix: noUnblockToJump (#1893) 2023-10-26 00:29:26 +02:00
Vendicated
1502024440
Fix iLoveSpam 2023-10-26 00:29:03 +02:00
Vendicated
45fa4f89c6
Fix NoScreensharePreview 2023-10-26 00:20:56 +02:00
Vendicated
96dff84ed8
Fix PlatformIndicators 2023-10-26 00:03:51 +02:00
Vendicated
c1b8739104
Fix RevealAllSpoilers 2023-10-26 00:01:49 +02:00
Vendicated
2183d2d29d
fix TimeBarAllActivities 2023-10-25 23:59:38 +02:00
Vendicated
722dcb033e
Fix RelationShipNotifier 2023-10-25 23:50:33 +02:00
Ben Richeson
53cd14844f
Fix MessageLogger (#1888)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-10-25 23:46:51 +02:00
nora
38daf6ec2b
ImageZoom: fix typo (#1832) 2023-10-25 23:37:07 +02:00
Amia
635b80c58b
fix: NoPendingCount (#1886) 2023-10-25 23:26:50 +02:00
Jack
ec9e111047
fix: (rewrite) SecretRingToneEnabler (#1887) 2023-10-25 23:25:39 +02:00
Jack
d54f8b5e8e
fix: BANger (#1885) 2023-10-25 23:25:02 +02:00
Vendicated
09a922a01c
Fix MuteNewGuild 2023-10-25 23:20:04 +02:00
Nuckyz
23963f40c2
Fix IgnoreActivities 2023-10-25 18:12:28 -03:00
Vendicated
a41abfef31
Fix MemberCount 2023-10-25 23:06:30 +02:00
Vendicated
ddee3e1264
Fix VencordToolbox 2023-10-25 23:00:11 +02:00
Aubrey/オーブリー
6c4afa52a3
Fix titlebar duplication with native Windows titlebar (#1890)
Co-authored-by: V <vendicated@riseup.net>
2023-10-25 22:49:41 +02:00
Vendicated
ce6081c39b
Merge branch 'main' into dev 2023-10-25 22:49:09 +02:00
Nuckyz
123853e848
Fix ShowHiddenChannels 2023-10-25 17:25:44 -03:00
Vendicated
0c6445b66b
fix reporter 2023-10-25 21:06:18 +02:00
Vendicated
7094345516
fix lintttttttttttttttttt :3 2023-10-25 21:01:20 +02:00
Vendicated
148a32096c
Fix PronounDB in profile 2023-10-25 20:51:37 +02:00
TheKodeToad
ab1b002ed1
TypingTweaks: fix (#1883) 2023-10-25 20:43:48 +02:00
rini
c7a20769f9
fix things using lodash (#1882) 2023-10-25 20:29:32 +02:00
Amia
9c13befcb6
fix: ServerProfile (#1881) 2023-10-25 20:24:31 +02:00
Vendicated
3fdd0edf14
Fix InvisibleChat 2023-10-25 20:24:17 +02:00
Vendicated
2169090ce5
Fix SpotifyCrack 2023-10-25 20:20:46 +02:00
redstonekasi
5e13de72c2
fix: BlurNSFW (#1880) 2023-10-25 20:16:57 +02:00
Vendicated
3a7c27253b
Fix PermissionFreeWill 2023-10-25 20:14:39 +02:00
Vendicated
465a87f66b
Fix ColorSighted 2023-10-25 20:07:45 +02:00
Amia
13c59f47cb
fix: MutualGroupDMs (#1879) 2023-10-25 20:01:25 +02:00
Vendicated
0d7157dd20
Fix BetterNoteBox 2023-10-25 19:59:20 +02:00
Vendicated
88c6e2a0e9
Fix PermissionViewer 2023-10-25 19:54:13 +02:00
Vendicated
535d510d3b
Fix LoadingQuotes 2023-10-25 19:47:29 +02:00
Vendicated
cbc23b1bdd
Fix BetterGifAltText 2023-10-25 19:41:18 +02:00
Vendicated
aa2a57b733
Fix Dearrow 2023-10-25 19:33:39 +02:00
Vendicated
c3ccbbfa99
Fix SpotifyControls 2023-10-25 19:28:07 +02:00
Vendicated
685f44d40f
Fix OpenInApp 2023-10-25 19:22:57 +02:00
Vendicated
8f0009778a
Fix ViewIcons 2023-10-25 19:09:59 +02:00
rini
f385dc380e
fix whoreacted, betteruploadbutton (#1877) 2023-10-25 19:09:37 +02:00
redstonekasi
bb900785ed
fix: ShowAllMessageButtons (#1876) 2023-10-25 18:52:21 +02:00
redstonekasi
61fac0a8f1
fix: NoPendingCount (#1871)
Co-authored-by: V <vendicated@riseup.net>
2023-10-25 18:46:32 +02:00
Jack
1a607639cc
fix: greetStickerPicker (#1874) 2023-10-25 18:43:28 +02:00
Vendicated
c90440f031
Fix Settings Proto 2023-10-25 18:42:31 +02:00
Vendicated
baf952512c
Fix InvisChat, PreviewMsg, SendTimestamps, SilentMsg, SilentType, Translate 2023-10-25 18:35:03 +02:00
rini
fbc5038306
fix: RPC plugins (#1873) 2023-10-25 18:20:32 +02:00
Vendicated
ddc39fe84d
Fix SettingStores, GameActivityToggle 2023-10-25 18:15:18 +02:00
redstonekasi
4f8c75372c
fix: NoBlockedMessages (#1870) 2023-10-25 18:09:04 +02:00
Jack Matthews
024a77c577 fix: typingIndicator 2023-10-25 18:07:41 +02:00
Vendicated
1130521e4b
Fix WebContextMenus 2023-10-25 18:07:12 +02:00
redstonekasi
3bd657611c fix: StartupTimings (#1869) 2023-10-25 17:38:55 +02:00
Syncx
b659d4e9c1 fix: FavoriteEmojiFirst (#1844) 2023-10-25 17:38:55 +02:00
rini
9c60b38acc Fix PinDMs, SMYN, SilentTyping, ValidUser, PlatformIndicators (#1865) 2023-10-25 17:38:55 +02:00
sunnie
c5dd50ad8f fix messageLinkEmbeds, moreUserTags (#1859) 2023-10-25 17:38:55 +02:00
redstonekasi
131e91a37c fix: AlwaysTrust (#1868) 2023-10-25 17:38:55 +02:00
Nico
06b4dffa62 forceOwnerCrown: fix (#1858) 2023-10-25 17:38:55 +02:00
Syncx
3917193e8f fix: ImageZoom (#1864) 2023-10-25 17:38:55 +02:00
redstonekasi
8d1561aed4 fix: NoticesAPI (#1863) 2023-10-25 17:38:55 +02:00
Dea
cf3c28e1ff fix: onePingPerDM (#1867) 2023-10-25 17:38:55 +02:00
Vendicated
af1aa39647 Delete RNNoise - Krisp is now on web, so this is obsolete 2023-10-25 17:38:55 +02:00
megumin
7de1b5dcb6 fix: sortFriendRequests patches (#1848) 2023-10-25 17:38:55 +02:00
megumin
cd06980016 fix: pronoundb profile popout (#1860) 2023-10-25 17:38:55 +02:00
Nico
e69236c5f8 fix(vcDoubleClick): update for new discord build (#1862) 2023-10-25 17:38:55 +02:00
AutumnVN
2478ffb695 fix userutils (#1861) 2023-10-25 17:38:55 +02:00
Vendicated
788d22f9e9 MessageDecorationsAPI: fix 2023-10-25 17:38:55 +02:00
Vendicated
b69ac1cdad Fix PlatformIndicators 2023-10-25 17:38:55 +02:00
redstonekasi
892a79b2a7 fix: CommandsAPI 2023-10-25 17:38:55 +02:00
Vendicated
9895540943 MemberListDecoratorAPI: Fix 2023-10-25 17:38:55 +02:00
Vendicated
7e0fc1d0ea ServerListAPI: fix 2023-10-25 17:38:55 +02:00
Vendicated
bb771153e3 MessageEvents: Fix 2023-10-25 17:38:55 +02:00
TheKodeToad
65d39dcf21 ShowConnections, NoProfileThemes: Fix (#1854) 2023-10-25 17:38:55 +02:00
Jack
cf5e93ee52 fix: fakeProfileThemes (#1837) 2023-10-25 17:38:55 +02:00
megumin
4d8e4e62ca fix: notrack failing patches (#1857) 2023-10-25 17:38:55 +02:00
AutumnVN
cb2532d22c disableDMCallIdle: fix (#1856) 2023-10-25 17:38:55 +02:00
AutumnVN
8c998b9330 experiments: fix (#1855) 2023-10-25 17:38:55 +02:00
Vendicated
fb22c57da1 MessageAccessoriesAPI: Fix 2023-10-25 17:38:55 +02:00
Vendicated
554bb18e9b BadgeAPI: fix 2023-10-25 17:38:55 +02:00
Vendicated
44c9675795 ContextMenuApi: fix 2023-10-25 17:38:55 +02:00
AutumnVN
322ecc5e88 usrbg: fix (#1853) 2023-10-25 17:38:55 +02:00
Vendicated
7ee9a8bb99 SpotifyControls: Fix 2023-10-25 17:38:55 +02:00
Vendicated
69b54535c3 Fix NoTrack 2023-10-25 17:38:55 +02:00
AutumnVN
2a56081bc2 callTimer: fix (#1850) 2023-10-25 17:38:55 +02:00
AutumnVN
baa7d8c078 noDevToolsWarning: fix (#1851) 2023-10-25 17:38:55 +02:00
AutumnVN
940193c30b noMosaic: fix (#1849) 2023-10-25 17:38:55 +02:00
Hugo C
09b646b860 fix: PictureInPicture (#1839)
Co-authored-by: V <vendicated@riseup.net>
2023-10-25 17:38:55 +02:00
Syncx
922e3ce6fe fix: FavoriteGifSearch (#1842) 2023-10-25 17:38:55 +02:00
Erik
cb93c11e16 Fix CustomRPC (#1846) 2023-10-25 17:38:55 +02:00
megumin
4beef9f73b fix: ComponentDispatch and GifPaste plugin (#1843)
Co-authored-by: V <vendicated@riseup.net>
2023-10-25 17:38:55 +02:00
Vendicated
4da79abb21 Fix components filter 2023-10-25 17:38:55 +02:00
Vendicated
4e27722b54 fix webpack patching 2023-10-25 17:38:55 +02:00
Vendicated
97c0face2f
PinDMs: Fix canary crash 2023-10-25 00:38:02 +02:00
V
1a1d9b07e8
Fix canary crashing (#1833) 2023-10-25 00:17:11 +02:00
Nuckyz
4a2def03e7
Fix MessageDecorationsAPI patch 2023-10-23 22:42:23 -03:00
Vendicated
544edce9f9
bump to v1.5.8 2023-10-21 19:26:29 +02:00
Dea
e4485165d0
onePingPerDM: add settings (#1802)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Dea <dea-banana@riseup.net>
2023-10-21 18:42:37 +02:00
Macintosh II
fada76ec81
PlatformIndicators: make size same as other memberlist icons (#1789)
Co-authored-by: V <vendicated@riseup.net>
2023-10-21 18:41:56 +02:00
zImPatrick
f659c46031
FakeNitro: Add app icon customization (#1822)
Co-authored-by: V <vendicated@riseup.net>
2023-10-21 17:53:00 +02:00
Nuckyz
ae1dc4eab0
Make reporter ignore useless Discord errors (#1829) 2023-10-21 17:51:07 +02:00
Nuckyz
fe60a72b80
Fix NoPendingCount patch 2023-10-21 12:26:04 -03:00
Nuckyz
5a0b2ee3f5
Fix FakeNitro patch 2023-10-21 12:26:04 -03:00
Nuckyz
6c1b8b0d8a
Fix MessageDecorationsAPI 2023-10-21 12:25:57 -03:00
Nuckyz
b2a1410a96
Remove useless Experiments patch 2023-10-21 12:03:54 -03:00
Nuckyz
c25c95eecd
Fix IgnoreActivities making reporter angry 2023-10-21 12:00:09 -03:00
ioj4
d94418f42f
fix: windows host update patching (#1820) 2023-10-19 11:14:40 +02:00
Vendicated
da1a8cdd67
web: Fix themes tab 2023-10-19 10:13:05 +02:00
Vendicated
5d7ede34d8
bump to v1.5.7 2023-10-19 01:23:59 +02:00
Vendicated
cd61354998
ContributorModal: Fix vertical overflow on multi word names 2023-10-19 00:22:49 +02:00
AutumnVN
a452945ac8
feat(plugin): NoTypingAnimation (#1680) 2023-10-19 00:14:14 +02:00
Marocco2
b577660800
feat(VcNarrator): add {{NICKNAME}} as placeholder (#1792) 2023-10-19 00:05:47 +02:00
AutumnVN
4f57c7eded
betterNotes, userVoiceShow: fix padding issue (#1804) 2023-10-18 23:54:35 +02:00
Ryan Cao
e3e5da10a9
fix: Content Security Policy patching (#1814)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-10-18 23:44:29 +02:00
Vendicated
998ce72f3b
ForceOwnerCrown: Remove log spam 2023-10-14 02:46:57 +02:00
AutumnVN
188d12d1a3
roleColorEverywhere: role group color in thread/forum (#1778) 2023-10-13 04:26:18 +02:00
AutumnVN
a522eab40d
feat(plugin): NoMosaic (#1791) 2023-10-13 04:10:36 +02:00
AutumnVN
c2721f158f
imageZoom: fix again (#1793)
Co-authored-by: V <vendicated@riseup.net>
2023-10-13 04:07:21 +02:00
Vendicated
61cd7b4d99
arrpc: refactor for use in vesktop 2023-10-13 03:49:58 +02:00
Marocco2
926af0d1cd
feat(VcNarrator): add {{DISPLAY_NAME}} as placeholder (#1642)
Co-authored-by: V <vendicated@riseup.net>
2023-10-12 04:05:46 +02:00
sunnie
dcaf4aec97
fix moreUserTags (#1780) 2023-10-12 03:30:32 +02:00
Nuckyz
5a97adb435
Fix broken IgnoreActivities patch 2023-10-10 05:10:46 -03:00
V
925d709335
bump to v1.5.6 2023-10-09 03:57:44 +02:00
V
1a36dbbc9b
ci: cleanup publish workflow 2023-10-09 03:56:41 +02:00
V
b59db2f8c2
Drop Firefox extension support
Despite me already fixing all issues, mozilla is still giving me more
trouble. Now they are asking me to provide them with testing credentials
for discord. Not only do i not want to give them my account, it also
isn't even possible because of how discord's login from new location
verification works

i am very tired of having to fight mozilla and their stupid guidelines /
requests. publishing to amo is a nightmare. as such, official support
for the extension is hereby dropped

we cannot even distribute the extension ourselves because extensions
NEED TO BE SIGNED to install them (unless you use firefox nightly).
and guess how you sign? VIA THEIR STUPID STORE

Options for firefox users:
- use the UserScript
- grab extension-firefox.zip from releases and install it on firefox
  nightly
- make your own firefox developer account and manually sign the
  extension-firefox.zip and pray they sign it for you (they wouldn't
  sign my unlisted upload of it)
- use a chromium browser
2023-10-09 03:49:33 +02:00
V
d81302f64c
Revert mozilla store compliance changes
This reverts commit 97b6699afefe373d510dda5589a0754a4b380153.

Vencord is dropping support for the firefox extension, so these changes
are now obsolete. revert so users can still install the extension
manually and enjoy the full experience
2023-10-09 03:15:43 +02:00
V
390987e4a9
Remove ReviewDB
abuse / harassment has gotten pretty bad.
i proposed the ability to allow users to delete comments from their own
profile, but there seems to be no interest among the reviewdb owners.

i don't want vencord to harbour harassment, so i'm removing the plugin
for now
2023-10-09 03:09:56 +02:00
V
377cf60055
fix global settings listeners 2023-10-09 03:01:54 +02:00
Nico
34ac718705
fix(forceOwnerCrown): update broken patch (#1777) 2023-10-07 23:20:36 -03:00
Nuckyz
e4659ed7c3
Rewrite IgnoreActivities (#1693) 2023-10-07 23:04:17 -03:00
Vendicated
c33d59b45d
serverProfile: fix crash with lurked guilds
Co-authored-by: aamiaa <9750071+aamiaa@users.noreply.github.com>
2023-10-08 03:49:00 +02:00
Vendicated
ac1b67ccbd
Experiments(isStaff): Fix search history not showing 2023-10-07 23:00:44 +02:00
Vendicated
c0f2c97458
ReviewDB: proper multi account support 2023-10-06 19:43:24 +02:00
Vendicated
664dd0a992
ReviewDB: allow deleting reviews on own profile 2023-10-06 18:44:22 +02:00
Vendicated
f66e35b658
fix(SendTimestamps): Do not add to ReviewDB input 2023-10-06 18:05:53 +02:00
Vendicated
df214e1e93
chore: remove legacy code 2023-10-06 18:01:19 +02:00
Vendicated
47a39a062e
Fix Vesktop SettingsCog context menu 2023-10-06 17:54:46 +02:00
Vendicated
9e63da6d78
bump to v1.5.5 2023-10-06 04:08:49 +02:00
AutumnVN
79295683ee
PictureInPicture: pip button hover styles (#1775)
Co-authored-by: V <vendicated@riseup.net>
2023-10-06 04:07:16 +02:00
Vendicated
5eb9dd04df
Fix member list decorations api 2023-10-06 04:00:09 +02:00
Vendicated
03b5dc9c27
BetterRoleDot: Fix ci test false positives 2023-10-06 03:17:44 +02:00
wntiv-main
726a1b5d96
Fix command API (#1776)
Co-authored-by: V <vendicated@riseup.net>
2023-10-06 03:16:21 +02:00
AutumnVN
581fe252a4
fix imageZoom (#1772) 2023-10-03 02:39:34 +02:00
Vendicated
30b2e88e77
Bump to v1.5.4 2023-10-03 02:29:57 +02:00
TheKodeToad
1f38a8eeab
Fix WhoReacted (#1769) 2023-10-03 02:26:38 +02:00
Vendicated
6db9721c06
ReviewDB: fix usericons appearing over modals 2023-10-03 02:17:28 +02:00
Dea
9891791fa7
feat(plugin): onePingPerDM (#1757)
Co-authored-by: V <vendicated@riseup.net>
2023-10-03 01:53:14 +02:00
Lewis Crichton
8dd5eeead2
feat(plugin): PermissionFreeWill (#1763)
Co-authored-by: V <vendicated@riseup.net>
2023-10-03 01:26:57 +02:00
whqwert
abf8667a5d
fix(plugin): Party mode 🎉 2023-10-02 23:17:51 +02:00
Vendicated
e33ac900bc
Merge remote-tracking branch 'origin/main' into dev 2023-10-02 23:10:07 +02:00
V
8a026060c7
Update blank.yml 2023-09-29 19:01:55 +02:00
V
c8b77bb187
Update bug_report.yml 2023-09-29 19:00:50 +02:00
AutumnVN
88b06191b9
fix modal image + reviewdb bot tag (#1761)
* fix modal image

* fix reviewdb bot tag
2023-09-29 00:46:33 +02:00
Vendicated
62277770a8
bump to v1.5.3 2023-09-28 02:42:55 +02:00
V
4facc3cad7
webContextMenus: support pasting images 2023-09-27 04:13:40 +02:00
TheKodeToad
837d1fc083
web: fix themes tab (#1756) 2023-09-26 21:47:12 +02:00
V
608a67c9ae
fix quick/searchReply & MessageClickactions not working in dms 2023-09-26 03:54:59 +02:00
AutumnVN
f32d25b641
viewRaw, viewIcons, permissionsViewer: fix some edge cases (#1745)
Co-authored-by: V <vendicated@riseup.net>
2023-09-26 01:48:09 +02:00
V
4c7a2ba340
Merge branch 'main' into dev 2023-09-26 01:45:38 +02:00
V
da7f0cfff6
PluginModal: Fix cancel button being white on white in light theme 2023-09-26 01:38:46 +02:00
V
ae6584da7c
add os-accent-color variable (following BetterDiscord) 2023-09-25 18:53:10 +02:00
V
c6b1b9463c
improve webpack find error messages 2023-09-25 18:27:18 +02:00
V
376aaf39ce
fix SearchReply filter 2023-09-25 18:07:43 +02:00
V
5454a41243
update issue templates 2023-09-25 04:27:16 +02:00
Hugo C
044f64e446
Improve permission checks on several plugins (#1746)
Co-authored-by: V <vendicated@riseup.net>
2023-09-24 16:42:53 +02:00
V
0b7fca864a
plugin READMEs: migrate from imgur to github assets 2023-09-24 16:17:33 +02:00
V
30ac256070
migrate all plugins to folders 2023-09-24 16:02:18 +02:00
Sam
d0e2a32471
VcNarrator: Ignore multiple underscores (#1748)
Co-authored-by: V <vendicated@riseup.net>
2023-09-24 15:55:23 +02:00
V
ec026ca34c
Update bug_report.yml 2023-09-24 15:45:22 +02:00
V
4baaa9bd91
Delete .github/ISSUE_TEMPLATE/feature_request.yml 2023-09-24 15:44:02 +02:00
V
d9933c5793
settingsSync: correctly use platform agnostic relaunch 2023-09-24 01:16:50 +02:00
V
2735037a67
[skip ci] bump to v1.5.2 2023-09-24 01:12:00 +02:00
Dziurwa
83bfe28fa4
FriendInvites: Add picking uses (1 or 5) (#1727)
Co-authored-by: V <vendicated@riseup.net>
2023-09-23 03:50:14 +02:00
V
08c5d23636
add max attempts to lazys 2023-09-23 03:25:19 +02:00
V
ac0f834155
Fix Plugin Settings on canary 2023-09-23 03:15:07 +02:00
V
fa16e1b56f
fix updater 2023-09-22 15:58:29 +02:00
V
cfca393f2b
ci(generatePluginList): add filePath 2023-09-22 04:48:54 +02:00
Nuckyz
eacc673bcc
Fix BetterRoleDot making reporter angry 2023-09-21 23:28:40 -03:00
Nuckyz
dba6c4cea6
Fix broken FakeNitro patch (again) 2023-09-21 22:41:25 -03:00
V
6b10947d06
Bump to v1.5.1 2023-09-21 18:58:43 +02:00
V
97b6699afe
Fuck you Mozilla 2023-09-21 18:56:58 +02:00
V
7e91edc757
browser: unhardcode rnnoise 2023-09-21 18:19:59 +02:00
V
a82544e93e
Delete FixInbox: Discord fixed this issue themselves now 2023-09-21 17:57:14 +02:00
AutumnVN
d8c8b74ed7
BetterRoleDot: Setting for copying role color in profile popout (#1698) 2023-09-21 17:40:37 +02:00
Daniel Foster
fadd1598f5
Settings: use nearest-neighbour for shiggy (#1739)
Co-authored-by: V <vendicated@riseup.net>
2023-09-21 17:18:41 +02:00
Manti
e5c0898dd6
[ReviewDB] add emojis, discord markdown & notifications (#1718)
Co-authored-by: V <vendicated@riseup.net>
2023-09-21 17:16:15 +02:00
Nuckyz
9550b74b2a
Fix broken FakeNitro patch 2023-09-20 02:44:31 -03:00
V
6cfb67a52e
fix ci 2023-09-19 04:15:34 +02:00
V
12fe367385
bump to v1.5.0 2023-09-19 04:12:54 +02:00
AutumnVN
fc0417eb80
moreUserTags: fix user tag in member list (#1730) 2023-09-19 04:11:30 +02:00
AutumnVN
1f87d14ab2
textReplace: pad space only in string rules (#1738) 2023-09-19 04:11:30 +02:00
XAlboX
7d8f3508a8
ClearURLs: Add si parameter on youtube.com (#1733)
Co-authored-by: V <vendicated@riseup.net>
2023-09-19 04:11:30 +02:00
V
41f5d71e38
Bundle dependencies with extensions for webstore rule compliance (#1740) 2023-09-19 04:11:27 +02:00
AutumnVN
efb88a4df8
favGifSearch: fix search bar (#1732) 2023-09-19 04:10:15 +02:00
Syncx
a73d09a2f0
PreviewMessage: Add attachments (& misc changes) (#1715) 2023-09-12 23:14:17 +02:00
AutumnVN
cf7c4d63b6
pictureInPicture: don't show PiP button on normal file (#1725) 2023-09-12 23:11:53 +02:00
Archer
a95311ef2c
lastfm: Add setting for artist name and song title only (#1726)
Co-authored-by: V <vendicated@riseup.net>
2023-09-12 23:11:25 +02:00
lovenginx
dd23f9802c
InvisibleChat: fixup decryption modal (#1720) 2023-09-12 23:04:50 +02:00
Nuckyz
f23ddf4cae
oopsies 2023-09-12 05:10:44 -03:00
Nuckyz
4222c7fd9f
Fix broken FakeNitro patch on canary 2023-09-12 05:01:18 -03:00
Nuckyz
09f65b401e
Fix broken MessageLogger patches 2023-09-12 04:28:55 -03:00
V
7364776715
Dearrow: Fix button part 2 2023-09-09 19:51:30 +02:00
V
3a5b70d410
Dearrow: Fix button 2023-09-09 19:49:11 +02:00
V
e08d49edac
New Plugin: Dearrow (#1723) 2023-09-09 19:17:50 +02:00
Mushrrom
17abbd3e3e
LastFM: Add setting for using name + artist as activity name (#1713)
Co-authored-by: V <vendicated@riseup.net>
2023-09-09 04:22:41 +02:00
V
25a1d934c6
bump to v1.4.7 2023-09-09 00:22:24 +02:00
Luna
620319a4bc blurnsfw: fix crash (#31)
Reviewed-on: https://codeberg.org/Ven/cord/pulls/31
Co-authored-by: Luna <imlvnaa@gmail.com>
Co-committed-by: Luna <imlvnaa@gmail.com>
2023-09-08 21:55:27 +00:00
Hugo C
4c9996d620
feat(plugin): PictureInPicture (#1697)
Co-authored-by: V <vendicated@riseup.net>
2023-09-08 03:57:44 +02:00
V
885c75fdaa
add custom plugin author popouts (#1712) 2023-09-08 03:42:20 +02:00
Lewis Crichton
f2a22c5e57
feat: crash info in /vencord-debug (#1714)
Co-authored-by: V <vendicated@riseup.net>
2023-09-08 03:41:30 +02:00
V
5e3a485edc
ci: generate plugin readme map 2023-09-08 02:26:47 +02:00
Luna
452bf72e56 rnnoise: fix error on webcam (#29)
errored because there was no audio stream, luckily the patched function had a boolean indicating if it was audio or not so just ignore it if it isnt

Reviewed-on: https://codeberg.org/Ven/cord/pulls/29
Co-authored-by: Luna <imlvnaa@gmail.com>
Co-committed-by: Luna <imlvnaa@gmail.com>
2023-09-06 17:10:33 +00:00
Luna
afa47addd7 usrbg: export the data to allow other plugins to access (#30)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: Ven <ven@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/Ven/cord/pulls/30
Co-authored-by: Luna <imlvnaa@gmail.com>
Co-committed-by: Luna <imlvnaa@gmail.com>
2023-09-06 17:10:04 +00:00
V
8ecee3d09f
fix ViewRaw & VoiceMessages context menu style 2023-09-06 19:08:48 +02:00
luk1337
d2aac5edc5
ClearURLs: Add share_id parameter on reddit.com (#1706) 2023-09-06 18:53:28 +02:00
lovenginx
fc45510053
LoadingQuotes: add a new quote (#1710) 2023-09-06 18:52:14 +02:00
AutumnVN
77bfaf38a5
HideAttachments: fix broken css (#1707) 2023-09-06 18:51:48 +02:00
AutumnVN
a4cadc03d8
viewRaw: dont add context menu to guild folders (#1708) 2023-09-06 18:51:18 +02:00
AutumnVN
315fcf1972
messageLogger: make ignoreChannels also ignore threads (#1709) 2023-09-06 18:50:20 +02:00
Nuckyz
f1b3b3c0a3
Uncanonicalize reported patches 2023-09-06 18:47:54 +02:00
V
4c805d08be
[skip ci] PatchHelper: Fix copy button style 2023-09-05 21:37:39 +02:00
V
9b987d1e56
Bump to v1.4.6 2023-09-05 21:25:16 +02:00
V
774318d583
ViewRaw: Update description 2023-09-05 21:24:43 +02:00
V
7d954f9ade
ViewRaw: Fix ugly copy icon & context menu position 2023-09-05 21:20:13 +02:00
Luna
860d6edc7b HideAttachments: allow hiding stickers (#13)
Self explanatory

Reviewed-on: https://codeberg.org/Ven/cord/pulls/13
Co-authored-by: Luna <imlvnaa@gmail.com>
Co-committed-by: Luna <imlvnaa@gmail.com>
2023-09-05 18:56:28 +00:00
Rini
223b0366c6 pinDMs: fix alt+shift+up (#25)
applies the same fix as alt+up/down to the jump to unread one

confirmed non-explosion of shc

Reviewed-on: https://codeberg.org/Ven/cord/pulls/25
Co-authored-by: Rini <rini@rinici.de>
Co-committed-by: Rini <rini@rinici.de>
2023-09-05 18:51:22 +00:00
Luna
69cb7593eb View Raw on more objects (#27)
Shows view raw on guilds, channels, and users

Reviewed-on: https://codeberg.org/Ven/cord/pulls/27
Co-authored-by: Luna <imlvnaa@gmail.com>
Co-committed-by: Luna <imlvnaa@gmail.com>
2023-09-05 18:49:57 +00:00
V
8614e17633
Fix some plugins wrongly displaying the settings cog 2023-09-05 20:10:42 +02:00
Ryan Cao
aecd9d8fda
feat(memberCount): format count according to user locale (#1679) 2023-09-05 19:46:33 +02:00
Hugo C
faeb4fb585
previewMessage: Hide the button once a message is sent (#1692)
Co-authored-by: V <vendicated@riseup.net>
2023-09-05 19:45:44 +02:00
dolfies
0d18b44ba7
feat(plugin): ShowTimeouts (#1687)
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Co-authored-by: V <vendicated@riseup.net>
2023-09-05 19:42:35 +02:00
omahs
d671bd65ac
fix typos (#1703) 2023-09-05 19:40:32 +02:00
AutumnVN
8e952c630b
MessageLogger: fix ignore by id doesn't ignore edited (#1705) 2023-09-05 19:39:22 +02:00
V
2c758ccdf8
new plugin: ServerProfile (#1704)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-09-05 18:36:14 +02:00
Nuckyz
c165725297
Fix OpenInApp patch 2023-09-02 03:03:50 -03:00
Nuckyz
29fbe3701a
Fix CrashHandler patch 2023-09-02 03:03:31 -03:00
V
0b7c0e9587
new plugin: themeAttributes (#1686) 2023-08-31 01:49:53 +02:00
Cynthia Foxwell
d88524e8cf
ShowMeYourName: Fix username being wrong on slash commands (#1690)
Co-authored-by: V <vendicated@riseup.net>
2023-08-31 01:01:29 +02:00
Hugo C
d6efd99849
messageLinkEmbeds: fix AutoModEmbed on compact mode (#1691)
Co-authored-by: V <vendicated@riseup.net>
2023-08-31 01:00:16 +02:00
ruukulada
fe6be987fd
gameActivityToggle: Fix disabled SVG look (#1696) 2023-08-31 00:53:05 +02:00
V
d688075c0a
Update Screenshot 2023-08-29 01:12:47 +02:00
V
c752be45b2
fix(security - ipc): do not expose sender 2023-08-25 14:57:04 +02:00
V
07c1f5eed1
Fix theme upload on crapOs 2023-08-25 14:37:50 +02:00
V
4df01b1e62
ci: fix generatePluginList 2023-08-25 14:33:33 +02:00
AutumnVN
ebe10d3fad
customRPC fixes (#1666)
Co-authored-by: V <vendicated@riseup.net>
2023-08-25 14:30:12 +02:00
Adrian Castro
eca4af829f
feat: add copyUserURLs plugin (#1683)
Co-authored-by: V <vendicated@riseup.net>
2023-08-25 14:24:58 +02:00
CanadaHonk
60458cdf1f
Win32Updater: use before-quit event instead of patching updater (#1668) 2023-08-25 14:21:27 +02:00
Sefa Eyeoglu
714d87241c
build: allow overriding git details & disabling updating (#1677)
Co-authored-by: V <vendicated@riseup.net>
2023-08-25 14:15:02 +02:00
Elliott Tallis
f628aa7a70
ClearURLs: Add si parameter on youtu.be (#1681) 2023-08-25 14:03:58 +02:00
megumin
0f0282551d
🗿 but HD (#1678) 2023-08-25 14:02:10 +02:00
V
0335e1ca59
new plugin FixSpotifyEmbeds: less loud embeds 2023-08-21 16:59:32 +02:00
V
817f0f7473
[skip ci] fix getPluginTarget 2023-08-20 15:14:28 +02:00
V
98a03c8862
bump to 1.4.5 2023-08-19 01:44:11 +02:00
V
72ce7a5ad1
themes: ignore non theme files 2023-08-19 01:33:39 +02:00
Syncx
e699ea63c7
feat(plugin): PreviewMessage (#1607)
Co-authored-by: V <vendicated@riseup.net>
2023-08-19 01:19:10 +02:00
VMGuy23
97e1e9eb7a
messageLogger: apply deleted style to ephemeral messages (#1608)
Co-authored-by: V <vendicated@riseup.net>
2023-08-19 01:14:51 +02:00
bb010g
4c4036546a
new plugin: NormalizeMessageLinks (#1641) 2023-08-19 01:10:59 +02:00
CanadaHonk
d582e61ec3
Fix patching Win32 updater with OpenAsar (#1667)
This is more generic rewrite allowing for more paths to be added in the future for whatever reason (like a rename in future Discord versions).

(The "OpenAsar" code previously was completely wrong)
2023-08-19 00:54:35 +02:00
V
ccd2ce8baf
Bump to 1.4.4 2023-08-16 02:10:06 +02:00
V
ede507e80c
chore: update for 'Vencord Desktop' -> 'Vesktop' rebrand 2023-08-16 01:55:56 +02:00
Justice Almanzar
ffdf63563b
feat(plugins): Web/Vesktop AI Noise Suppression powered by RNNoise (#1477)
Co-authored-by: V <vendicated@riseup.net>
2023-08-16 01:32:11 +02:00
Sammy
55b755b2df
InvisibleChat: improve encryption validator (#1646) 2023-08-16 00:57:56 +02:00
Nuckyz
ca439e9e9a
Fix ReviewDB buttons 2023-08-15 19:21:16 -03:00
Nuckyz
e02fcce3dc
Fix ServerListAPI 2023-08-15 18:48:50 -03:00
V
3e732646e5
Fix plugins broken by latest canary update (#1637)
* Fix WebContextMenus
* BetterGifAltText: futureproof find
* Fix pronoundb profile patch
* Fix NoScreensharePreview
* Fix FakeNitro emote text patch

---------

Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-08-12 05:27:59 +02:00
V
d5b3b51050
bump to v1.4.3 2023-08-12 03:02:55 +02:00
V
725fb27e54
futureproof settings patches 2023-08-12 03:01:53 +02:00
V
243381fc91
Settings: Give vencord sections classes so themes can match them 2023-08-12 02:59:24 +02:00
V
54cbdfdad0
MemberCount: Remove negative margin, fixes issues with some themes 2023-08-12 02:46:38 +02:00
V
fe80b8cc85
AnonymiseFileNames: Properly keep .tar.* extensions 2023-08-12 02:45:11 +02:00
Andrew-DLO
742f5cf556
feat(plugin): SecretRingToneEnabler (#1570)
Co-authored-by: V <vendicated@riseup.net>
2023-08-12 02:23:00 +02:00
fawn
3b3da90c44
feat(VencordToolbox): add quickcss toggle (#1604)
Co-authored-by: V <vendicated@riseup.net>
2023-08-12 02:14:07 +02:00
TheKodeToad
c79e065d09
ShowMeYourName: Option to use display names instead of usernames (#1634)
Co-authored-by: V <vendicated@riseup.net>
2023-08-12 01:53:06 +02:00
V
3b8b43c7e0
ci: Fix reporter
Co-authored-by: Justice Almanzar <superdash993@gmail.com>
2023-08-12 01:36:19 +02:00
AutumnVN
6e7996659f
messageLogger: fix ingore guild (#1632) 2023-08-11 06:10:49 +02:00
Arrow
abdf4ebb05
feat(plugin): NoSentryConsole (#1623)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-08-11 00:17:29 +02:00
Justice Almanzar
fa124d8877
build: split preload & renderer.css into D. desktop / vesktop (#1629) 2023-08-11 00:14:50 +02:00
im-h
135da2a5f3
Translate/SendTimestamps: Fix accessibility (#1628)
Co-authored-by: V <vendicated@riseup.net>
2023-08-10 02:31:28 +02:00
Manti
c96a1a9998
[ReviewDB] hide blocked users (#1626)
Co-authored-by: V <vendicated@riseup.net>
2023-08-10 00:28:46 +00:00
Rini
8b6c8bc0ec chore: transition to shorter license headers (#6)
Transition from the GPL jumpscare to a more concise header:

	/*
     * Vencord, a Discord client mod
	 * Copyright (c) 2023 Vendicated and contributors
     *
	 * SPDX-License-Identifier: GPL-3.0
     */

Old files still keep the old headers, while eslint will now autofix the new header

Reviewed-on: https://codeberg.org/Ven/cord/pulls/6
Co-authored-by: Rini <rini@rinici.de>
Co-committed-by: Rini <rini@rinici.de>
2023-08-10 00:17:49 +00:00
V
1a62249da6
bump to v1.4.2 2023-08-04 20:20:07 +02:00
Ryan Cao
21318850b1
fix: open links externally in Quick CSS editor (#1487)
Co-authored-by: V <vendicated@riseup.net>
2023-08-04 20:13:53 +02:00
Lewis Crichton
885ad134b3
fix(cloud): cross-client enabling without proper validation (#1531) 2023-08-04 20:10:12 +02:00
V
3e7d4e2623
make QuickCSS have priority over themes 2023-08-04 20:08:13 +02:00
AutumnVN
d3691f74c4
chore: Fix greetStickerPicker file name (#1610) 2023-08-04 20:01:49 +02:00
Justice Almanzar
268f3a1840
add bottom margin to settings section (#1616) 2023-08-04 19:56:40 +02:00
megumin
d6c43986fd
Add proper user-friendly theme manager (#635)
Co-authored-by: Justice Almanzar <superdash993@gmail.com>
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: V <vendicated@riseup.net>
2023-08-04 19:52:20 +02:00
V
bb7deeb09c
UserVoiceShow: Fix error 2023-08-03 00:13:04 +02:00
AutumnVN
0407be9847
textReplace: fix edge case (#1521)
Co-authored-by: V <vendicated@riseup.net>
2023-08-02 00:28:44 +00:00
Hugo C
645749b5ae
VoiceMessages: Improve permission check (#1544)
Co-authored-by: V <vendicated@riseup.net>
2023-08-02 00:27:10 +00:00
AutumnVN
2e002107a6
customRPC: add validation & some fixes (#1481)
Signed-off-by: V <vendicated@riseup.net>
2023-08-02 01:52:08 +02:00
Nickyux
cc07518a34
VoiceMessages: Support replies (#1565)
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Co-authored-by: Justice Almanzar <superdash993@gmail.com>
Co-authored-by: V <vendicated@riseup.net>
Signed-off-by: V <vendicated@riseup.net>
2023-08-02 01:51:39 +02:00
Lewis Crichton
ea64b33e24
star history :3 (#1528) 2023-08-01 05:35:45 +02:00
V
1a92d3ff8d
bump to v1.4.1 2023-08-01 05:34:43 +02:00
Rawir
45bb1af011
MuteNewGuild: Support lurked guilds (#1546)
Co-authored-by: Rawiros <45668076+Rawiros@users.noreply.github.com01~>
2023-08-01 05:32:29 +02:00
V
39ad88f433
Experiments: Fix canary 2023-08-01 05:23:52 +02:00
V
8cf4d2a2c0
Update 1_INSTALLING.md 2023-07-28 16:44:17 +02:00
V
fe5e041db8
VoiceMessages: Read file from dynamic path (fixes mac & linux support) 2023-07-27 02:06:18 +02:00
V
d18681c197
Delete blank.yml 2023-07-27 01:35:58 +02:00
V
c024db1bc4
Update config.yml 2023-07-27 01:35:44 +02:00
V
d8a0db8bee
Update blank.yml 2023-07-27 01:34:41 +02:00
V
f62efa5aa7
Update blank.yml 2023-07-27 01:33:55 +02:00
Hugo C
1d77ab0ade
MemberCount: fix family center crash (#1486) 2023-07-26 00:33:00 +00:00
V
9268cf3ffb
Bump to 1.4.0 2023-07-26 02:17:20 +02:00
Syncx
208371c471
feat(plugin): Favorite Gif Search (#1386)
Co-authored-by: V <vendicated@riseup.net>
2023-07-26 01:50:24 +02:00
Ryan Cao
c69c6f8cb7
feat(MessageLinkEmbeds): add whitelist/blacklist modes (#813)
Co-authored-by: V <vendicated@riseup.net>
2023-07-26 01:41:41 +02:00
alexia
f2c6fcaa3b
fix(PronounDB): don't use guild pronouns in global profile modal (#1462) 2023-07-26 01:34:51 +02:00
Aayush Shah
abf62f28db
Themes tab: Add QuickCss button (#1475) 2023-07-26 01:29:57 +02:00
V
8620a1d86d
New plugin: VoiceMessages (#1380)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Justice Almanzar <superdash993@gmail.com>
2023-07-26 01:27:04 +02:00
V
198b35ffdc
Merge branch 'main' into dev 2023-07-26 01:25:30 +02:00
Ryan Cao
b4d0d95731
fix vencord toolbox being unusable with drag region (#1480)
* fix(OpenInApp): Broken patch (#1434)

* fix: vencord toolbox unreachable with drag region

---------

Co-authored-by: whqwert <94757998+whqwert@users.noreply.github.com>
2023-07-25 15:34:57 +02:00
whqwert
f785aa1473
fix(OpenInApp): Broken patch (#1434) 2023-07-17 02:05:20 -03:00
Commandtechno
d56e6560e5
[chore] Update DisableDMCallIdle description (#1422) 2023-07-16 00:51:14 +02:00
Justice Almanzar
a7e74ee4d5
classNameFactory: Allow (& ignore) all sorts of falsy values (#1427) 2023-07-16 00:50:21 +02:00
Luca Zeuch
1340f023a3
feat(MessageLogger): add option to ignore channels and guilds (#1420) 2023-07-14 18:21:29 +02:00
V
2bf0c324d7
chore: Update dev ids 2023-07-14 01:12:07 +02:00
V
f621cdb50b
Bump monaco editor 2023-07-14 01:10:53 +02:00
echo
9717001783
delete uwuifier plugin (#1414)
Co-authored-by: exhq <exhq@exhq.dev>
2023-07-14 01:08:27 +02:00
MrDiamondDog
065ab75627
Add "Show New" option in plugin settings (#1416)
Co-authored-by: V <vendicated@riseup.net>
2023-07-13 19:35:40 +02:00
V
8aea72c1be
FakeNitro: Fix crash 2023-07-10 22:39:40 +02:00
V
bea7a1711e
Bump to v1.3.4 2023-07-08 03:40:31 +02:00
Lewis Crichton
e52ae62441
feat(cloud): support multiple user accounts (#1382)
Co-authored-by: V <vendicated@riseup.net>
2023-07-08 03:36:59 +02:00
V
7cd1d4c60f
translate: Add context menu item; fix MLE compatibility 2023-07-08 03:30:16 +02:00
V
2a318e390e
QuickCss: Fix wrongly applying quickcss when editing while disabled 2023-07-08 03:13:32 +02:00
V
7c7723bfb1
Plugin Settings: Use Switches for booleans 2023-07-08 03:04:58 +02:00
dolfies
2db0e71e5b
fix(RelationshipNotifier): Ignore user-actioned friend requests (#1390) 2023-07-08 02:37:32 +02:00
Ryan Cao
cde8074f44
feat(ClearURLs): add Threads share link tracking param (#1384) 2023-07-08 02:34:16 +02:00
Nuckyz
8b1630bc99
Fix ShowAllMessageButtons (#1392)
Co-authored-by: V <vendicated@riseup.net>
2023-07-08 02:33:37 +02:00
V
bf34b2ae43
Bump to v1.3.3 2023-07-06 01:26:31 +02:00
V
cb5f23d9b5
ProxyLazy: Limit attempts 2023-07-06 00:54:49 +02:00
V
cd2cbfa0ef
ShowHiddenChannels: Fix crashing on canary 2023-07-06 00:43:43 +02:00
Ryan Cao
232e340fab
fix: send notification when settings are manually synced (#1378)
Co-authored-by: V <vendicated@riseup.net>
2023-07-04 15:59:42 +00:00
V
8027daa2b0
Fix types 2023-07-04 17:58:32 +02:00
Ryan Cao
0f7b9f588e
perf(cloud sync): minify synced settings (#1377) 2023-07-04 17:57:28 +02:00
V
93482ac2a5
SpotifyControls: improve open in app capabilities & styles 2023-07-04 17:53:17 +02:00
V
994c3b3c92
OpenInApp: Fix bot buttons 2023-07-04 17:52:52 +02:00
V
c696c186e8
WebKeybinds: Fix scope 2023-07-02 01:23:51 +02:00
V
30d5e2108f
Bump to 1.3.2 2023-07-02 01:22:53 +02:00
V
1eabd1b701
WebKeybinds: Fix broken zoom on vesktop 2023-07-02 01:22:37 +02:00
V
1cbf2b43e1
Fix Badges 2023-07-02 01:17:02 +02:00
V
4c197d5d51
New Plugin: WebKeyBinds 2023-07-02 01:08:06 +02:00
V
f89027f46a
Bump to 1.3.1 2023-06-30 17:08:57 +02:00
V
07a0ebb1d2
PronounDB: Fix crash when having pronouns in profile disabled 2023-06-30 17:08:43 +02:00
V
f09b44b0d5
VcNarrator: Ignore stage channels 2023-06-30 15:55:20 +02:00
V
b607eebcb7
ImageZoom: Add square lens option 2023-06-30 15:50:56 +02:00
V
0936ca2985
ValidUser: Fix mentions with ! (<@!...>) 2023-06-30 15:43:21 +02:00
V
13bde79ec8
PronounDB: Fix profile patch, add pronoun source to tooltip 2023-06-30 15:39:33 +02:00
V
b592defaaf
OpenInApp: Add Epic Games; properly respect settings 2023-06-30 15:16:42 +02:00
V
73354973a3
OpenInApp: Support steam; integrate with SpotifyControls & ShowConnections 2023-06-29 16:15:07 +02:00
V
e12c0e546c
PronounDB: Fix crash 2023-06-29 14:28:16 +02:00
V
088a8bd1b6
OpenInApp: Support profile activity on web & profile connection 2023-06-29 05:02:13 +02:00
V
51adb26d01
New plugin: OpenInApp - Open spotify urls in spotify app 2023-06-29 04:33:35 +02:00
Nuckyz
cb980a1cad
Fix broken SHC patches (#1360) 2023-06-28 03:45:16 +00:00
V
69b10c1f07
Bump to 1.3.0 2023-06-27 23:12:56 +02:00
alexia
8e9ba7c7ee
PronounDB: fix caching not respecting user preference (#1344) 2023-06-27 22:46:52 +02:00
Commandtechno
12e3c9234d
ViewIcons: Fix animated server pfps & profile edit button (#1350)
fixes #1236
2023-06-27 22:41:52 +02:00
Sammy
1d8dcef394
InvisibleChat: fix indicator & crashes (#1356) 2023-06-27 22:41:33 +02:00
Syncx
4fe2845234
ImageZoom: add nearest neighbour (#1341) 2023-06-27 22:41:19 +02:00
V
5e71ed286e
PronounDB: Strip newlines from Discord Pronouns (Discord is stupid) 2023-06-25 19:08:18 +02:00
V
5edbd2391d
Fix build 2023-06-25 18:33:44 +02:00
ActuallyTheSun
8472c3823e
feat(🗿): ignore blocked users (#1327)
Co-authored-by: V <vendicated@riseup.net>
2023-06-25 18:31:39 +02:00
V
2103e52115
WebContextMenus: Support all text areas 2023-06-25 18:22:36 +02:00
Nuckyz
afbfb641e8
Fix IgnoreActivities broken patch (#1337) 2023-06-25 09:57:18 +00:00
V
d7ac418e05
Fix some plugins displaying legacy discriminators (username#0000) 2023-06-23 18:09:43 +02:00
V
214c101740
Bump to v1.2.9 2023-06-22 22:45:28 +02:00
V
5a0e501829
Fix ValidUser, BetterFolders & MutualGroupDms
Co-authored-by: Juby210 <31005896+Juby210@users.noreply.github.com>
Co-authored-by: Amia <9750071+aamiaa@users.noreply.github.com>
2023-06-22 22:35:59 +02:00
V
92113da7c0
Fix Vencord Web 2023-06-21 21:50:28 +02:00
V
96f30a5359
Update README.md 2023-06-21 21:07:48 +02:00
V
ceb1f15188
utils: export missing members 2023-06-21 21:04:04 +02:00
V
626eb3613e
[skip ci] WelcomeStickerPicker: Clean up settings types 2023-06-21 02:05:01 +02:00
V
3020fcc9bb
[skip ci] Improve typings for settings.withPrivateSettings 2023-06-21 02:00:59 +02:00
heckeroncrack
bc0de3926c
Improve uwuify (#1295)
Co-authored-by: V <vendicated@riseup.net>
2023-06-20 22:11:59 +02:00
Nuckyz
9820b79dfe
SHC: show active now voice members; PermViewer: improve styling (#1314) 2023-06-20 22:11:50 +02:00
V
ab811470fc
Update README.md 2023-06-18 21:27:52 +02:00
V
e4162e7bd5
PronounDB: Source pronouns from both PronounDB & Discord (#1301)
* PronounDB: Source pronouns from both PronounDB & Discord

* jdsjdsajjasiofigvjodsjigfdjiogegjnegjnersjn
2023-06-17 03:50:59 +02:00
V
7e8397a4da
Bump to 1.2.8 2023-06-16 19:47:01 +02:00
V
555cf64080
BiggerStreamPreview: Hide element if no stream is found 2023-06-16 19:46:39 +02:00
V
2039e10fd5
Codeberg Mirror: Hopefully fix race condition 2023-06-16 19:45:01 +02:00
Phil
e8d90d2b45
feat(plugin): BiggerStreamPreview (#1222)
Co-authored-by: V <vendicated@riseup.net>
2023-06-16 19:35:50 +02:00
kb
55af40ee74
MessageLogger: Add user ignore list (#1275)
Co-authored-by: V <vendicated@riseup.net>
2023-06-16 19:35:35 +02:00
V
a1fabcdf0a
UnsuppressEmbeds: Support dms 2023-06-16 19:28:30 +02:00
V
eaeb60308e
[skip ci] Add version to /vencord-debug 2023-06-16 19:07:22 +02:00
Amia
662c0227eb
New Plugin: MutualGroupDMs (#1239)
Co-authored-by: V <vendicated@riseup.net>
2023-06-15 03:39:15 +02:00
Dominik
543fdf4943
New Plugin: UnsuppressEmbeds (#1262)
Co-authored-by: V <vendicated@riseup.net>
2023-06-15 03:18:34 +02:00
Nuckyz
1225383723
Fix broken SHC patch (#1290) 2023-06-15 01:05:15 +00:00
V
07a9adbce2
🧹🧹 2023-06-13 03:45:05 +02:00
V
42d8211871
Fix disabling plugins whose stop() errors 2023-06-13 02:29:29 +02:00
V
ab3e993274
ViewRaw: Remove less properties from author 2023-06-13 02:23:06 +02:00
rad
386dfe363a
ViewRaw: Add setting for swapping left/right click (#1263)
Co-authored-by: V <vendicated@riseup.net>
2023-06-13 01:05:04 +02:00
AAGaming
a4191c9f6c
Settings: add custom sections support (#1270)
Co-authored-by: V <vendicated@riseup.net>
2023-06-11 23:11:56 +02:00
V
f1349a2787
Remove Cloud Sync notification 2023-06-11 22:57:07 +02:00
V
3680c26f72
Bump to 1.2.7 2023-06-09 22:18:32 +02:00
Axu
683c92f904
fix: do not highlight new member leaf in red in message logger (#1071)
Co-authored-by: V <vendicated@riseup.net>
2023-06-09 22:17:44 +02:00
Rendundakat
3410ed024f
QuickReply: respect users in userList of NoReplyMention (#1191)
Co-authored-by: V <vendicated@riseup.net>
2023-06-09 22:17:32 +02:00
Luna
dbad10984a [skip ci] Don't run ci on forks; Fix interaction nicks in SMYN (#4)
The following actions are disabled unless run on the main repo: codeberg, publish, build/upload, reportBrokenPlugins

Reviewed-on: https://codeberg.org/Ven/cord/pulls/4
Co-authored-by: Luna <imlvnaa@gmail.com>
Co-committed-by: Luna <imlvnaa@gmail.com>
2023-06-09 19:48:18 +00:00
whqwert
55543d8640
Fix(RevealAllSpoilers): New spoiler class name (#1229) 2023-06-04 17:22:21 -03:00
Manti
263fbc377e
Fix ReviewDB auth (#1227) 2023-06-03 20:07:04 +00:00
AutumnVN
c9c0ab5aca
Fix cloud auth [object%20Object] (#1226) 2023-06-03 16:57:31 -03:00
V
7b2bf08b8f
[skip ci] ci: fix inconsistent formatting 2023-06-01 22:49:15 +02:00
TheKodeToad
43011825af
Add NoProfileThemes plugin (#1193)
Co-authored-by: V <vendicated@riseup.net>
2023-05-31 19:14:18 +02:00
fawn
4abcea61f8
feat(MessageClickAction): delete message logger history (#1204) 2023-05-31 19:14:17 +02:00
V
cba810cab5
Improve settings selectable text style 2023-05-31 19:14:17 +02:00
Amia
5938c7d67c
Plugin: NoPendingCount (#1216)
Co-authored-by: V <vendicated@riseup.net>
2023-05-31 19:14:17 +02:00
V
99d8b8b75f
VcNarrator: Fix always saying 'someone' 2023-05-31 19:14:14 +02:00
Luna
503d49d295 Add codeberg to gitlens and add build tasks (#3)
It needed to be added inorder for gitlens to recognise the remote as a gitea instance
without, it wouldnt be able to open branches/specific commits correctly

and build tasks are just for ease of development

(teeny weeny pr bc im not gonna do anything else with this branch)

Co-authored-by: Luna R <imlvnaa@gmail.com>
Reviewed-on: https://codeberg.org/Ven/cord/pulls/3
Co-authored-by: Luna <imlvnaa@gmail.com>
Co-committed-by: Luna <imlvnaa@gmail.com>
2023-05-31 17:12:37 +00:00
V
137b79d95b
README: Add codeberg mirror 2023-05-30 16:27:59 +02:00
V
3c02d6e1b4
Bump to 1.2.6 2023-05-30 15:24:21 +02:00
V
a2a33ca62d
Fix occasional freezing on firefox (cache related) 2023-05-30 15:23:33 +02:00
V
d8cd557fb2
Clean up build scripts 2023-05-30 15:23:32 +02:00
V
7568bbaed0
VcNarrator: Improve username cleaning to support non ascii chars 2023-05-30 15:23:32 +02:00
V
9023d45d9e
Improve /vencord-debug 2023-05-30 15:23:32 +02:00
V
bee70390a9
Add codeberg mirror sync workflow 2023-05-29 23:50:45 +02:00
Manti
3e3d05fc26
ReviewDB: Add Review Modal & Pagination (#1174)
Co-authored-by: V <vendicated@riseup.net>
2023-05-28 22:03:06 +02:00
V
6300198a54
MessageLinkEmbeds: Fix niche unclaimed account bug
This plugin fires MESSAGE_UPDATE events for messages containing message
links (to rerender them). If the updated message is an interaction, it
contains message.interaction.user. If the one who ran the command is
you, message.interaction.user will be you and the email in this data is
always set to null. Discord seems to update the local user data with
this user. So essentially, in the above described edge case it would
update the current user to have no email (only locally, in memory. There
is 0 risk for your account, it was just a temporary visual bug) which
would cause the unclaimed account banner to appear.

This commit fixes this by simply omitting the interaction field from the
MESSAGE_UPDATE event
2023-05-28 22:02:47 +02:00
Nuckyz
458c7ed4c5
Make Fake Nitro transformations support the new markdown (#911) 2023-05-23 00:25:48 -03:00
V
d888a0a291
[skip ci] Fix plugin json generation 2023-05-23 04:42:06 +02:00
V
a94787a9f3
Bump to 1.2.5 2023-05-23 03:50:21 +02:00
V
368d2bcdbb
DiscordUtils: Add sendMessage 2023-05-23 03:47:09 +02:00
UwU
bc46bfa467
New plugin : Party mode 🎉 (#1161)
Co-authored-by: ArjixWasTaken <53124886+ArjixWasTaken@users.noreply.github.com>
Co-authored-by: V <vendicated@riseup.net>
2023-05-23 03:32:27 +02:00
V
dab48288a8
TypingTweaks: Fix type error 2023-05-23 03:22:48 +02:00
alexia
9aef97c771
fix(TypingTweaks): use global displayName over username (#1165)
Co-authored-by: V <vendicated@riseup.net>
2023-05-23 03:19:26 +02:00
PandaNinjas
9d62dec6b9
Uwufy: Add option to uwufy all messages (#1036)
Co-authored-by: V <vendicated@riseup.net>
2023-05-23 03:13:21 +02:00
V
6bf6583e7d
FakeNitro: Fix unavailable emotes; Discord Stickers are now free (#1184)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-05-23 03:02:48 +02:00
Nuckyz
5219fb700f
PermViewer: Add ability to change sort order; Properly center (#1182) 2023-05-23 02:22:25 +02:00
V
184c03b28e
PluginModal: Anonymise authors (#1176) 2023-05-23 01:55:39 +02:00
Nuckyz
ec091a7959
Fix SHC broken patches; Sort PermViewer channel overwrites roles (#1166) 2023-05-20 02:24:56 +02:00
V
89a6c575c9
lastfm: Fix discord application 2023-05-18 05:11:06 +02:00
V
60325c6aa5
bump to 1.2.4 2023-05-17 04:42:50 +02:00
Hunter
c2a1c4cbf6
Urban Dictionary: better embed (#1127)
Co-authored-by: V <vendicated@riseup.net>
2023-05-17 04:40:40 +02:00
Syncx
1d6b78f6c6
feat(plugin): FavoriteEmojiFirst (#1110)
Co-authored-by: V <vendicated@riseup.net>
2023-05-17 04:38:15 +02:00
Sammy
341151a718
feat(InvisibleChat): use discords embed api (#1162) 2023-05-17 04:38:01 +02:00
Nuckyz
f6fd7cf37a
PlatformIndicators: make mobile icon match Discord's (#1160) 2023-05-17 04:37:47 +02:00
Kay Alizer
d53476a32a
VcNarrator: Add 'Narrate Self' option (#1120)
* Add 'Narrate Self' option

if anyone wishes to hear their own name on join this should work

* Apply suggestions from code review

Co-authored-by: V <vendicated@riseup.net>

* Update vcNarrator.tsx

---------

Co-authored-by: V <vendicated@riseup.net>
2023-05-16 19:23:10 +02:00
Animal
fc943b7778
emoteCloner: allow other characters in sticker names (#1140)
* allow spaces and other characters in cloning sticker names

* fix

* stickers have a different character limit
2023-05-16 19:22:46 +02:00
Nuckyz
3f2bcd2cab
SHC: Permissions viewer integration (#475) 2023-05-16 17:15:56 +00:00
Nuckyz
235000cf41
Fix handleProtoChange erroring when userSettingsProto is undefined (#1150) 2023-05-16 14:53:17 +00:00
V
263884cbd8
PermViewer: Fix context menu for roleless users & muted channels (#1138)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-05-16 00:19:20 +02:00
V
bb83c0b672
[skip ci] PronounDB: Do not add pronouns to automod messages 2023-05-15 18:20:34 +02:00
V
2815509c00
UserScript: Fix fetch().res.ok 2023-05-15 02:49:34 +02:00
V
53ff2532f4
bump to 1.2.3 2023-05-15 02:34:01 +02:00
Nuckyz
64b38348d4
feat(plugins): Permissions Viewer (#477)
Co-authored-by: V <vendicated@riseup.net>
2023-05-15 02:33:04 +02:00
V
9c1b3a9afd
Update README.md (#1123) 2023-05-14 13:16:18 +02:00
outfoxxed
caf77a3d7f
NoReplyMention: add option to only exclude specific users from pings (#1107)
Co-authored-by: V <vendicated@riseup.net>
2023-05-14 00:01:10 +00:00
Manti
7a27de8927
[ReviewDB] Improve UI & Use new RewviewDB api endpoints (#1104) 2023-05-14 01:29:13 +02:00
LordElias
1bc0678422
MoreUserTags: Fix & Add ability to customize text inside tag (#1077)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: ActuallyTheSun <78964224+ActuallyTheSun@users.noreply.github.com>
2023-05-13 22:38:49 +00:00
V
cd53cf38fe
ReverseImageSearch: Add engine icons 2023-05-14 00:10:01 +02:00
V
f13f9e80a9
ViewIcons: Add context menu icons 2023-05-13 23:49:47 +02:00
V
c062f9bdeb
SpotifyControls: Add context menu icons 2023-05-13 23:47:13 +02:00
V
f2ef96a420
PlatformIndicators: Fix profile spacing 2023-05-13 22:56:46 +02:00
V
16365d3ea1
bump to 1.2.2 2023-05-13 18:48:40 +02:00
V
1ec28a345b
EmoteCloner: Add Sticker cloning (#1118) 2023-05-13 18:47:46 +02:00
V
2fdc00b11e
BadgesAPI: Fix canary crash (new pomelo badge, the horror) 2023-05-12 23:34:30 +02:00
V
3da112680d
oops, add missing else 2023-05-12 04:17:12 +02:00
V
1d93162036
MessageClickActions: Add double click to reply 2023-05-12 04:15:35 +02:00
V
7dcd32e838
PlatformIndicators: Fix weird spacing in badges 2023-05-12 03:54:57 +02:00
V
ade31f993b
Implement plugin tags 2023-05-12 03:41:15 +02:00
AutumnVN
3c7496ac6d
TextReplace: Visible Linebreak in settings (#1063)
Co-authored-by: V <vendicated@riseup.net>
2023-05-12 02:59:51 +02:00
Supertiger
63387a48ee
silentMessageToggle: Add setting to disable auto disable (#1062)
Co-authored-by: V <vendicated@riseup.net>
2023-05-12 00:58:53 +00:00
V
3bb68467bb
Update README.md 2023-05-12 02:40:31 +02:00
V
2b337eace1
[skip ci] ShowHiddenChannel: Fix broken patch 2023-05-12 02:30:51 +02:00
V
5c5b009c41
Settings: Fix resetting scroll/search when getting a ping (#1106) 2023-05-12 01:40:43 +02:00
Nuckyz
0c54b1fa1d
[skip ci] Fix InvisibleChat button being added to wrong chat box (#1100) 2023-05-11 17:44:33 +00:00
Kode
393f76749a
USRBG: Hide Nitro badge if banner's source is USRBG (#1096)
* Hide Nitro badge if banner's source is USRBG, tweaks to description

* Rename function

* Update src/plugins/usrbg/index.tsx

---------

Co-authored-by: V <vendicated@riseup.net>
2023-05-11 19:33:08 +02:00
V
1fe7f3c297
ViewIcons: More consistent context menu position 2023-05-11 19:26:55 +02:00
V
622e8dc3e0
[skip ci] Translate: Shift/Right click shortcut 2023-05-11 19:00:18 +02:00
V
a8b6aea26f
bump to 1.2.1 2023-05-11 02:39:59 +02:00
V
e50c2fafa5
Settings > Plugins: Hide API plugins 2023-05-11 02:38:59 +02:00
V
6e3cafce42
Translate: Only add button in chat box 2023-05-11 02:35:06 +02:00
V
4d0a064425
WebContextMenus: Fix slate menu 2023-05-11 02:35:05 +02:00
V
d1ad6c47a7
Fix ServerListAPI 2023-05-11 02:35:05 +02:00
V
d5c35055f3
experiments: Add a warning card to experiments page (#1097) 2023-05-11 02:34:39 +02:00
V
cb385d1b28
New Plugin: Translate (#1089)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-05-10 23:14:04 +02:00
V
195f1a032f
ShowConnections: Add verified & copy/link icons in tooltip (#1092) 2023-05-10 23:13:47 +02:00
V
dfda9e7f84
New plugin: ValidUser (#1081) 2023-05-10 23:13:28 +02:00
Vendicated
0d5e2d0696
[skip ci] Refactor utils 2023-05-06 01:36:00 +02:00
Vendicated
2834bed518
MemberCount: Do not count offline group lol 2023-05-06 01:06:48 +02:00
Vendicated
0b2c3c834a
MemberCount: Fix not properly updating on channel switch 2023-05-05 18:41:04 +02:00
AutumnVN
3a54a24c70
VencordToolbox: Change img icon to svg (#1059)
Co-authored-by: V <vendicated@riseup.net>
2023-05-05 16:41:01 +00:00
Vendicated
244d10dc9e
[skip ci] Fix handleComponentFailed spam 2023-05-05 16:57:58 +02:00
Đỗ Văn Hoài Tuân
c25bc0ff4b
USRBG: Add VoiceBackground (#1038)
Co-authored-by: V <vendicated@riseup.net>
2023-05-05 02:54:33 +02:00
Kode
22334663cf
TextReplace: Test rules section (#1039) 2023-05-05 02:51:01 +02:00
Đỗ Văn Hoài Tuân
8813f81bde
ShowConnections: Remove "(click to copy/open)" from tooltip (#1041) 2023-05-05 02:47:59 +02:00
Đỗ Văn Hoài Tuân
84371ed456
TextReplace: Fix Linebreak (#1057) 2023-05-05 02:47:31 +02:00
Nuckyz
8f61119b99
ShowAllMessageButtons plugin (#1029) 2023-05-05 02:47:26 +02:00
JSEverything
0a89d09727
style(Settings): Make the text about raw theme links bold (#1052) 2023-05-05 02:47:18 +02:00
Nuckyz
474932161f
Fix FakeNitro Stickers (#1048) 2023-05-05 02:47:08 +02:00
Vendicated
bd95a25f4c
TextReplace: Do not apply rules in #textreplace-rules channel 2023-05-02 04:39:27 +02:00
Vendicated
6a57ecc22b
Delete FxTwitter & NoCanaryMessageLinks: Use TextReplace 2023-05-02 04:37:16 +02:00
Vendicated
0d665b7e0b
Badge duplication glitch was too good to be true 2023-05-02 03:11:40 +02:00
Vendicated
d94b28fb8e
Fix VencordToolbox 2023-05-02 02:57:25 +02:00
V
bc1d8694d4
new plugin: VencordToolbox (#998) 2023-05-02 02:55:38 +02:00
V
7bc1362cbd
MessageEvents: Support sendMessage with attachments (#1023) 2023-05-02 02:55:21 +02:00
Vendicated
4dce836ff7
RelationshipNotifier: Fix false positives when using AccountSwitcher 2023-05-02 02:52:09 +02:00
Vendicated
9f534c0685
SupportHelper: Add OpenAsar info 2023-05-02 02:52:03 +02:00
V
c62d05e1b3
Refactor ipc to be strongly typed and hide impl details (#1018) 2023-05-02 02:50:51 +02:00
Nuckyz
6a1cb133cd
Fix blank errors on plugin reports (#1021) 2023-05-01 07:26:27 +00:00
Vendicated
c6196dff81
[skip ci] PlatformIndicator: Fix 1px misalignment 2023-04-30 21:47:33 +02:00
Vendicated
acdb390302
[skip ci] Bump deps 2023-04-30 17:11:38 +02:00
Nuckyz
08d88b326d
Fix Plugin Reporting false dependency errors and other stuff (#1017) 2023-04-30 13:34:38 +00:00
LordElias
a73858d131
UserVoiceShow: Fix lack of bottom padding for popout on current user and broken modal patch (#873) 2023-04-30 10:29:45 -03:00
Đỗ Văn Hoài Tuân
b0caa6f4db
feat(plugin): TextReplace (#994)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-04-30 01:19:08 +00:00
Vendicated
168d4b4cd9
PlatformIndicators: Fix layout reflows - The trilogy 2023-04-30 02:07:57 +02:00
Vendicated
06cee75a56
PlatformIndicators: Fix layout reflows 2: Electric Boogaloo 2023-04-30 01:56:12 +02:00
Vendicated
d589d22a0b
bump to 1.2.0 2023-04-30 01:27:08 +02:00
Vendicated
4c13521a30
PlatformIndicators: Fix layout reflows 2023-04-30 01:25:17 +02:00
Manti
043381963b
ReviewDB: make warning review disableable; add timestamps (#948) 2023-04-30 00:53:37 +02:00
LordElias
5b485806ea
Plugin Settings: Better margin for plugin settings modal (#1008) 2023-04-30 00:52:20 +02:00
Kode
29c994648b
ShowConnections plugin (#923)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-04-30 00:49:27 +02:00
Vendicated
a35b417194
BetterRoleDot: Fix copying colour 2023-04-30 00:09:15 +02:00
Vendicated
070aa343ef
Remove migratePluginSettings calls 2023-04-30 00:09:15 +02:00
Đỗ Văn Hoài Tuân
b95c5c6619
Fix USRBG (#1000)
Co-authored-by: V <vendicated@riseup.net>
2023-04-30 00:08:04 +02:00
Vendicated
bf795c49df
[skip ci] Update Monaco; Fix code scanning errors 2023-04-29 04:16:28 +02:00
Vendicated
a2e03084b0
BadgeAPI: Add support for multiple donor badges 2023-04-29 02:06:52 +02:00
Vendicated
ec72b4c91d
PronounDB: Oop, conditional hooks are bad :> 2023-04-29 02:04:00 +02:00
Vendicated
d70d7c7b49
[skip ci] reporter: reduce sleep time 2023-04-28 19:34:21 +02:00
Vendicated
e7d0fc258d
[skip ci] WebContextMenus: fix false ci test positives 2023-04-28 19:32:29 +02:00
Vendicated
7b13b9a53e
PronounDB: Fix not working in profiles 2023-04-28 19:15:07 +02:00
Vendicated
1b2cb52dac
ViewIcons: Fix viewing banners 2023-04-28 18:36:22 +02:00
Vendicated
0fe0fecba2
MessageLogger: Nicer context menu 2023-04-28 04:23:42 +02:00
Vendicated
c1fca76f94
SilentMessageToggle: Add missing dependency on MessageEventsApi 2023-04-28 04:03:49 +02:00
carince
0cc3901e4e
MuteNewGuilds: add toggles for guild, everyone, and roles. (#979)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Norikiru <99774021+Norikiru@users.noreply.github.com>
2023-04-28 02:03:38 +00:00
Vendicated
f8ace5b53a
[skip ci] definePluginSettings: Add missing BigInt type 2023-04-28 02:57:09 +02:00
V
c0954a1844
Specify that theme in screenshot is only an example 2023-04-28 02:51:18 +02:00
V
6548163d3e
Update README.md 2023-04-28 02:49:34 +02:00
V
24f161d6e9
Update README.md 2023-04-28 02:48:39 +02:00
V
7d00b6a842
README: Add screenshot & more info 2023-04-28 02:17:33 +02:00
Vendicated
5f5d4b8961
[skip ci] UserScript: Fix cors check pt 2 2023-04-28 01:13:42 +02:00
Hazel Summer
5be86f9bd1
WhoReacted: Fix clicking user pfps also adding reaction (#977) 2023-04-24 01:42:38 +02:00
Vendicated
dfc3f05834
Fix RoleColorEverywhere crash 2023-04-23 23:33:55 +02:00
V
63fc354d48
feat: auto-managed flux subscriptions via plugin.flux (#959) 2023-04-22 01:18:19 +00:00
V
c6f0c84935
PinDMs: Fix alt up/down being weird (#957) 2023-04-22 03:17:00 +02:00
Vendicated
a8d017811d
TypingTweaks: Fix filter 2023-04-22 03:16:08 +02:00
Vendicated
8dd70f5d1a
Fix inserting text when markdown preview is off 2023-04-18 23:13:10 +02:00
Amsyar Rasyiq
8be6c6e3ce
fix(ReviewDB): fix margin in self-profile preview (#935)
Co-authored-by: V <vendicated@riseup.net>
2023-04-18 18:53:11 +02:00
V
7e96b5dcfb
RelationShipNotifier: Support multiple users (#944) 2023-04-18 16:52:46 +00:00
Đỗ Văn Hoài Tuân
99a7d78e9b
[skip ci] USRBG: Update README link to a more accurate one (#943) 2023-04-18 16:04:17 +02:00
Nuckyz
e70d00d008
BadgesAPI: Don't depend on getBadges module not being undefined (#942) 2023-04-18 05:25:14 +00:00
Nuckyz
c0ac6a4b86
SilentMessageToggle: Option to persist state (#941) 2023-04-18 04:54:21 +00:00
Vendicated
29749e93c7
CI Reporter fixes 2023-04-18 02:54:22 +02:00
Vendicated
993c6be219
Fix badges 2023-04-18 02:47:48 +02:00
Đỗ Văn Hoài Tuân
e2e1cf2bfd
feat(plugin): USRBG (#844)
Co-authored-by: amsyarasyiq <82711525+amsyarasyiq@users.noreply.github.com>
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-04-18 00:18:18 +02:00
Vendicated
59e3c2c609
ci: fix cding into wrong folder 2023-04-17 04:09:20 +02:00
Nuckyz
43d7ca4c30
Make chat bar buttons have a consistent size (#927)
Co-authored-by: V <vendicated@riseup.net>
2023-04-17 02:09:11 +00:00
V
5305447f44
firefox: Fix csp (QuickCss, themes, some plugins) (#554) 2023-04-17 04:05:01 +02:00
Vendicated
76e74b3e40
SendTimestamps: Fix chatbox having a scrollbar 2023-04-17 02:55:08 +02:00
Vendicated
e767da4b08
Fix errors on setups with no SpeechSynthesis support (part 2) 2023-04-17 01:52:46 +02:00
Vendicated
e4f3f57a28
bump to v1.1.7 2023-04-17 01:48:53 +02:00
Lily • Lylythii
72f6dd84ee
SilentTyping: Make Tooltip text casing consistent w other plugins (#918) 2023-04-17 01:46:51 +02:00
Mufaro
9c929a4d98
messageTags: Fix duplicate entries & replies (#922)
Co-authored-by: V <vendicated@riseup.net>
2023-04-16 23:44:33 +00:00
Luca LeBlanc
dac9cad873
Improve FakeNitro emoji popup message (#924)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: V <vendicated@riseup.net>
2023-04-17 01:43:11 +02:00
Vendicated
6fd5c7874f
Remove ContextMenuAPIs from plugin dependencies (now required) 2023-04-17 01:39:16 +02:00
Vendicated
a56dfe269c
Improve unintuitive plugin descriptions 2023-04-17 01:37:55 +02:00
Vendicated
7d55a81bac
MessageLogger: Add toggle deleted highlight rightclick button 2023-04-17 01:27:25 +02:00
Vendicated
ce64631310
MessageLogger: Apply styles to gifs, stickers & new markdown 2023-04-17 01:18:45 +02:00
Vendicated
1caaa78490
PinDMs: Add option to sort by most recent message 2023-04-17 01:06:54 +02:00
Vendicated
d35654b887
Improve themes tab instructions 2023-04-17 00:23:22 +02:00
Vendicated
ca5d24385f
Fix errors on setups with no Notifications/SpeechSynthesis support 2023-04-17 00:21:49 +02:00
V
cb3bd4b881
ci: Also upload extension zips 2023-04-15 15:02:31 +02:00
Lewis Crichton
ff3589d157
CloudSync: fix accidently applying stale settings (#915
* modify the cloud timestamp before importing

* use util/native relaunch

* lambda why

* "should work!" - Vendicated, 15th April 2023
2023-04-15 14:50:00 +02:00
dimden
7a98f1dfcb
Make Vencord Extension run in iframes (#914)
Fixes support for https://github.com/NeverDecaf/discord-PWA
2023-04-15 14:49:48 +02:00
fawn
9e6d3459e3
fix(smyn): reversed settings (#913) 2023-04-15 12:20:49 +02:00
Jack
ea30ca418f
feat: Add AlwaysAnimate plugin (#908)
Co-authored-by: V <vendicated@riseup.net>
2023-04-15 05:10:03 +00:00
Nuckyz
1f7ec93a24
SHC: Small improvements (#907) 2023-04-15 03:40:42 +00:00
Nuckyz
336c7bdd5e
SHC: Fix emoji rendering & allowed users/roles edge cases (#895)
Co-authored-by: V <vendicated@riseup.net>
2023-04-15 05:02:08 +02:00
V
88ad4f1b05
SendTimestamps (#891)
Co-authored-by: Tyler Flowers <contact@ggtylerr.dev>
2023-04-15 04:42:18 +02:00
Sofia Lima
f75f887861
feat(plugin): ShowMeYourName (#901) 2023-04-15 03:58:22 +02:00
Nuckyz
96f640da67
Make Context Menu API support hooks (#902)
Co-authored-by: V <vendicated@riseup.net>
2023-04-15 02:47:03 +02:00
Manti
e8809fc57b
add reviewdb bacc (#898) 2023-04-15 02:34:53 +02:00
Ryan Cao
ca91ef4e39
feat(moreUserTags): add HTML data attributes to user tags (#883)
Co-authored-by: V <vendicated@riseup.net>
2023-04-15 02:31:36 +02:00
V
db7fc3769b
Fix settings on Vencord Mobile (#905) 2023-04-15 02:27:11 +02:00
V
6c719f5ee9
PinDMs (#879) 2023-04-15 02:26:46 +02:00
Nuckyz
c6fd8cae16
Fix MuteNewGuild (#896) 2023-04-14 18:02:03 +02:00
V
1adbf9e41a
Delete muteNewGuild.ts
This plugin has a bad bug that breaks all DM notifications. Removed for now to prevent more damage, will be back once fixed!

I will try to make this future update fix the problem for any users affected by it.
For now, if you're affected, see https://gist.github.com/Vendicated/c86016b7fa68a754885ecd82a6ac0f2a for a quick fix
2023-04-14 08:53:40 +02:00
Nuckyz
aee6bed48c
Revert "Delete DisableDMCallIdle: Discord removed this 'feature'" (#889)
THEY ADDED IT BACK WHYYYY
2023-04-14 03:37:14 +02:00
Vendicated
c8817e805f
Fix badges 2023-04-13 21:04:19 +02:00
Vendicated
c6f0d0763c
Remove noisy notifications from notification log 2023-04-13 19:15:36 +02:00
Vendicated
3bd3012aa9
Delete DisableDMCallIdle: Discord removed this 'feature' 2023-04-13 19:12:09 +02:00
Vendicated
694a693a8e
MemberCount: Fall back to approx member count if necessary 2023-04-13 19:10:20 +02:00
Elliott Tallis
ed827c2d81
Shiki: Make CodeBlock button texts not copyable (#864) 2023-04-13 04:26:43 +02:00
Kode
71849cac9a
Fix ViewIcons on webhooks and default avatars! (#880)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-04-13 04:25:38 +02:00
Nuckyz
e34da54271
Option to transform emotes/stickers in compound messages (#876)
+ ContextMenu refactor to not call callbacks for same children multiple times

Co-authored-by: V <vendicated@riseup.net>
2023-04-13 04:22:38 +02:00
Vendicated
cfe41ef656
ViewIcons: Add format setting & user context menu + cleanup 2023-04-12 03:27:31 +02:00
V
4d836524c1
GreetStickerPicker: greet with stickers of your choice (#866)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-04-12 02:33:51 +02:00
Dziurwa
edc96387f5
Fix FriendInvites (#874) 2023-04-12 02:33:05 +02:00
Vendicated
358eb6ad8e
ImageZoom Fixes 2023-04-11 02:00:42 +02:00
Syncx
c997cb4958
ImageZoom: Fix not being able to close carousels on sides (#857)
Co-authored-by: V <vendicated@riseup.net>
2023-04-10 22:38:37 +00:00
Kode
83dab24fb9
PronounDB: Fix pronouns flickering (#862)
Co-authored-by: V <vendicated@riseup.net>
2023-04-11 00:37:24 +02:00
Vendicated
8a305d2d11
clean up spotify controls css 2023-04-11 00:32:11 +02:00
Ezzud
7eb12f0fb7
SpotifyControls: Fix flashing button row when using show on hover (#850)
Co-authored-by: V <vendicated@riseup.net>
2023-04-10 22:21:30 +00:00
Luna
0a3dc5c6e8
VoiceUserShow: Fix lack of bottom margin (#853)
Co-authored-by: V <vendicated@riseup.net>
2023-04-10 22:10:21 +00:00
Cat
b21516d44e
Make QuickCss window title consistent with other windows (#859)
Co-authored-by: V <vendicated@riseup.net>
2023-04-11 00:07:36 +02:00
Nuckyz
65f7cf9503
Nicer GameActivity & SilentMsg UX; Fix [object Object] jumpscare (#863) 2023-04-10 23:59:48 +02:00
V
40a7aa5079
UserScript: Fix cors check 2023-04-09 06:58:29 +02:00
Amsyar Rasyiq
c4a3d25d37
feat(NoAutoReplyMention): Inverse shift reply behaviour (#839)
Co-authored-by: V <vendicated@riseup.net>
2023-04-09 06:55:04 +02:00
Ryan Cao
613fa9a57b
feat: add translucency option for macOS (#849)
Co-authored-by: V <vendicated@riseup.net>
2023-04-09 02:06:09 +00:00
V
08822dd190
Improvements for VencordDesktop (#847) 2023-04-09 04:04:02 +02:00
Đỗ Văn Hoài Tuân
bfa20f2634
feat(setting): Disable minimum window size #834 (#848) 2023-04-09 03:41:55 +02:00
Vendicated
840da146b9
UX: Make possibly copy-relevant text in settings copyable 2023-04-08 23:28:12 +02:00
Vendicated
acc874c34f
WebContextMenus: Fix jpegs being uncopyable 2023-04-08 23:05:38 +02:00
Vendicated
0dee968e98
WebContextMenus: Don't include queryparams in filename on save 2023-04-08 22:57:34 +02:00
Vendicated
09e919f0c6
bump to 1.1.6 2023-04-08 03:53:32 +02:00
V
eaf1af75bd
WebContextMenus: Port more menus (#818)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-04-08 03:51:37 +02:00
exit
7c514e4b1d
SupportHelper: Add missing dependency - CommandsAPI (#823)
Co-authored-by: V <vendicated@riseup.net>
2023-04-07 19:19:12 +00:00
LordElias
1432baa28b
ignore userplugins when linting (#822)
Co-authored-by: V <vendicated@riseup.net>
2023-04-07 19:17:54 +00:00
exit
f1f61195c3
InvisibleChat: Add missing dependency on MessagePopoverAPI (#817)
Co-authored-by: V <vendicated@riseup.net>
2023-04-07 19:16:18 +00:00
Đỗ Văn Hoài Tuân
8fefa2b716
FakeNitro: Fix stickers with space in name #819 (#820) 2023-04-07 21:15:11 +02:00
Ryan Cao
2a0c30b66d
feat(moreusertags): add option to not show more tags for bots (#812)
Co-authored-by: V <vendicated@riseup.net>
2023-04-07 00:31:21 +00:00
Lewis Crichton
97f8d4d515
feat: Cloud settings sync (#505)
Co-authored-by: Ven <vendicated@riseup.net>
2023-04-07 02:27:18 +02:00
Vendicated
2672dea8e3
ci: bump action 2023-04-06 03:34:02 +02:00
Vendicated
63f5b0a663
bump pnpm to v8 2023-04-06 03:34:02 +02:00
ActuallyTheSun
e40ebacc5b
feat(plugin): WebhookTags -> MoreUserTags (#378)
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-04-06 03:28:38 +02:00
LordElias
e261c93563
feat(plugin): User Voice Show (#694)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-04-06 03:22:54 +02:00
Syncx
df7357b357
feat(plugin): Image Zoom (#510)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: Ven <vendicated@riseup.net>
2023-04-06 01:06:11 +00:00
Đỗ Văn Hoài Tuân
2e6c5eacf7
BetterFolders: Fix Close all not working (#808) 2023-04-06 03:02:53 +02:00
Dziurwa
c9fd404012
Fix FriendInvites (#802)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-04-05 23:01:11 +02:00
V
814302e272
Fix Badges (#801) 2023-04-05 22:45:14 +02:00
Anubis
72ba83924c
SpotifyControls: add album art hover transition (#797) 2023-04-05 22:45:03 +02:00
Nuckyz
9d742094cb
ShowHiddenChannels: Use Discord's new overlay vars (#795)
* Fix SHC css for new Discord vars

* I'm dumb

* improvements to work with themes

---------

Co-authored-by: V <vendicated@riseup.net>
2023-04-05 20:44:03 +00:00
Nuckyz
38f3aac98d
Fix VolumeBooster and improve ContextMenuAPI patch (#793)
Co-authored-by: V <vendicated@riseup.net>
2023-04-05 03:07:17 +00:00
Nuckyz
12ffb9d642
Fake Nitro Transform Stickers option and other stuff (#683)
Co-authored-by: V <vendicated@riseup.net>
2023-04-05 05:06:04 +02:00
Vendicated
99391a4f0e
fix generatePluginList 2023-04-05 04:54:54 +02:00
Vendicated
6492908a62
VencordDesktop: Fix Updater 2023-04-05 04:34:39 +02:00
Vendicated
676bc612d9
VencordDesktop: Include web plugins & use proper showItemInFolder 2023-04-05 04:09:42 +02:00
Vendicated
d8a5e43034
Fix Themes Tab 2023-04-04 22:24:16 +02:00
Vendicated
8ad710abca
Fix ContextMenuAPI 2023-04-04 22:19:52 +02:00
Vendicated
368cb7bc6b
Fix Toasts 2023-04-04 21:51:03 +02:00
Vendicated
4aa7a052d0
Bump to v1.1.5 2023-04-04 21:29:39 +02:00
Vendicated
f088f17a0a
Remove accidently introduced patch 2023-04-04 21:28:38 +02:00
Vendicated
a55c758b0e
Fix SpotifyControls 2023-04-04 21:27:44 +02:00
Vendicated
f092f434fe
Fix Vencord 2023-04-04 21:14:55 +02:00
Remty
2e6dfaa879
FakeProfileThemes: add usage guide (#778)
Co-authored-by: V <vendicated@riseup.net>
2023-04-04 13:28:41 +00:00
Nuckyz
96dc2e12d0
Fix Web & Game Activity Toggle (#777) 2023-04-04 15:26:53 +02:00
Đỗ Văn Hoài Tuân
d931790ed0
BetterFolders: Fix unread indicator & read all buttons being duplicated (#776) 2023-04-04 05:33:11 +02:00
V
6b26c12bfa
Add additional build flavours for Vencord Desktop (#765) 2023-04-04 01:16:29 +02:00
Vendicated
5bb08bdb64
SpotifyControls: Fix crashing on canary
Vencord is still pretty broken on Canary and likely will be for a bit,
but this should at least fix instantly crashing
2023-04-03 21:25:14 +02:00
Vendicated
405be7ef13
Fix weird style on username sheet 2023-04-03 03:13:54 +02:00
Vendicated
a7e2fb48ba
fix oopsie 2023-04-03 02:36:54 +02:00
Nuckyz
ae80749dd8
Game Activity Toggle and SettingsStoreAPI (#587) 2023-04-03 02:13:44 +02:00
Vendicated
8c47b7080d
QuickReply & Up Key: Do not attempt to edit/reply to logged deleted message 2023-04-02 22:14:58 +02:00
Juby210
8378638ee4
BetterFolders: fix mentions display (#761)
closes #759
2023-04-02 20:31:10 +02:00
V
7c563471f6
Fix typo 2023-04-02 18:31:23 +02:00
Juby210
29382d2781
Add BetterFolders plugin (#530)
Co-authored-by: Ven <vendicated@riseup.net>
2023-04-02 17:43:06 +02:00
Vendicated
6226672ee8
Web: Update extension icon from trolley to Vencord logo 2023-04-02 16:55:36 +02:00
V
5b5ee82f27
Update Contributor Badge to new logo 2023-04-02 16:16:15 +02:00
Remty
62f74f5917
feat(plugin): FakeProfileThemes (#710) 2023-04-02 16:12:19 +02:00
V
265c7a18a7
Delete corruptMp4s.ts
Discord/Electron fixed this bug, so mp4s created by this plugin just look normal on Electron 22, not fixable
2023-04-02 04:33:17 +02:00
Vendicated
462f191051
Bump to v1.1.4 2023-04-02 04:26:05 +02:00
V
6960a439c9
Add Notification log (#745) 2023-04-01 02:47:49 +02:00
Vendicated
4dff1c5bd5
RelationShipNotifier: Delay by 5s to fix false positives 2023-03-31 17:17:50 +02:00
nick
2c8ebdce7d
feat(plugin): RelationshipNotifier (#450)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-31 05:07:35 +00:00
Nuckyz
dae7cb67ef
Fix IgnoreActivities broken patch (#743) 2023-03-31 04:11:15 +00:00
Berlin
081b01b667
feat(plugin): Wikisearch (#585)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-31 04:09:19 +00:00
Vendicated
5340ea7ba0
Add back window transparency with temporary unsafe settings key 2023-03-31 05:59:45 +02:00
Vendicated
84a649a671
docs: fix ToC 2023-03-31 05:56:08 +02:00
Vendicated
efd9927696
Fix broken plugins 2023-03-31 05:55:25 +02:00
V
c86a34a15d
Update 1_INSTALLING.md 2023-03-31 05:30:45 +02:00
Vendicated
ff16513f21
Fix onHeadersReceived clashes when using OpenAsar (fix github raw styles) 2023-03-31 01:18:57 +02:00
Vendicated
906c265aea
FakeNitro: Fix fake emote rendering incorrectly in thread previews 2023-03-31 00:15:51 +02:00
Vendicated
708c16176b
Remove transparency feature
This not only causes incredibly confusion among users because they
expect it to work without themes, it also causes freezes/whitescreens
for some users. Thus, this feature is disabled for now until someone
contributes a fix!
2023-03-30 23:48:26 +02:00
whqwert
035d1e24b2
feat(SpotifyControls): Fix background color for built-in themes (#731)
Co-authored-by: V <vendicated@riseup.net>
2023-03-30 17:09:04 +02:00
Vendicated
48e9b1be7a
new Plugin: GifPaste - Insert Gif links instead of sending 2023-03-30 15:58:20 +02:00
Vendicated
6acdaf207d
NoTrack: Update description & authors 2023-03-30 01:41:18 +02:00
Vendicated
9d41b360c9
Fix NoTrack 2023-03-30 01:35:42 +02:00
Vendicated
12cbd73e7f
SpotifyControls: Add right click menus to title/album/artists 2023-03-30 01:29:34 +02:00
Phil
420b068094
Fix makeProxy returning stale proxies after assigning objects (#722) 2023-03-28 18:26:57 +00:00
Vendicated
ee943c4284
Bump to v1.1.3 2023-03-28 19:09:48 +02:00
Vendicated
337b3709d6
types: Make ErrorBoundary.wrap explicitly return Function 2023-03-28 19:06:58 +02:00
Elliott Tallis
eb318c678f
feat(ViewRaw): Improve View Raw action icon (#720)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-28 16:59:30 +00:00
Vendicated
081df6beb7
Fix SilentMessage/SilentTyping toggles showing in wrong sections
Closes #656
2023-03-28 18:56:12 +02:00
Vendicated
ab911b48b5
TypingTweaks: Make names open profile on click
Closes #718
2023-03-28 18:43:45 +02:00
Skye
8cb3491086
feat(uwuify): improve uwuification algorithm (#706)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-28 16:23:51 +00:00
Lewis Crichton
ee794d140f
fix: no more theme box obliteration (#707)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-28 16:20:06 +00:00
Vendicated
a00542b61b
MessageLinkEmbeds: Fix weird commas in title 2023-03-26 01:27:30 +01:00
Vendicated
041a13c9d3
DevCompanion: Always use original 2023-03-26 01:27:01 +01:00
Lewis Crichton
24aa90bd9c
fix API plugins being force enabled unconditionally (#704)
* only enable dependencies if required

* fixme note
2023-03-25 15:20:00 +00:00
Ven
c574f53417
Add Code of Conduct (#680) 2023-03-25 12:41:39 +00:00
Nuckyz
92b84a9e94
Fix broken patches (#701) 2023-03-25 08:42:26 +00:00
RuiNtD
bbf3c74cb2
Update LastFM plugin (#483)
Co-authored-by: Ven <vendicated@riseup.net>
Co-authored-by: Sofia Lima <me@dzshn.xyz>
2023-03-25 04:00:27 +01:00
hunter
93cb51a975
feat(MessageEvents): Promisable send/edit listeners (#514)
* promisable send/edit listeners

* added self

* Apply suggestions from code review

Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>

* fix patches

---------

Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-25 03:54:20 +01:00
Syncx
0b4ae729a3
feat(plugin): SearchReply (#551)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-03-25 02:37:29 +00:00
TheKodeToad
b90392576e
PronounDB: Add support for compact mode & clean up (#604) 2023-03-25 01:30:24 +00:00
Ven
e143260891
MessageLogger: Add context menu entry to remove history (#693) 2023-03-25 00:55:40 +00:00
Đỗ Văn Hoài Tuân
644c5c4faa
Make Vencord title look consistent with Discord (#685)
closes (#649)
2023-03-25 00:42:18 +00:00
Ven
8d8cedd72c
Also add Emote Cloner to Emote picker rightclick menu (#664) 2023-03-24 03:42:38 +01:00
Nuckyz
082ac62eda
feat(FakeNitro): Transform fake emojis into real ones (#669) 2023-03-23 10:45:39 +00:00
Nuckyz
7923a790e6
Fix MessagePopoverAPI and any error Fake Nitro client theme bypass might have (#665) 2023-03-23 02:11:28 -03:00
Vendicated
1368c25824
ci: Auto generate plugin json 2023-03-23 04:37:53 +01:00
iwa
d0b3678ad6
fix messagelogger deleted styles (#642)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-22 04:37:04 +01:00
Vendicated
cae8b1a93b
Bump to v1.1.2 2023-03-22 04:07:12 +01:00
Nuckyz
a1c1fec8cb
Improve Fake Nitro client themes bypass (#654) 2023-03-22 03:01:32 +00:00
Lewis Crichton
55a66dbb39
fix(RoleColorEverywhere): MessageLinkEmbeds DM error (#648) 2023-03-21 23:57:53 -03:00
Nuckyz
a2f0c912f0
Fix Fake Nitro making Discord unusable and ColorSighted not working sometimes (#640) 2023-03-21 09:41:31 +00:00
Nuckyz
e29bbf73aa
Fix Nitro Themes with FakeNitro (#639) 2023-03-21 09:03:28 +00:00
Nuckyz
0ba3e9f469
I'm sorry for hurting you Fake Nitro (#637) 2023-03-21 06:41:11 +00:00
Nuckyz
6f200e9218
Fix grammar and SHC patches matching wrong var (#636) 2023-03-21 06:30:09 +00:00
Nuckyz
586b26d2d4
Fixes and ShowHiddenChannels improvements (#634)
~ Fixes #630
~ Fixes #618
2023-03-21 06:07:16 +00:00
Ven
d482d33d6f
Fix the infamous MessageClickActions bug 2023-03-21 03:19:02 +01:00
Dossy Shiobara
37c2a8a5de
fix: settings input validation and error handling (#609)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-21 02:16:01 +00:00
Dossy Shiobara
265547213c
docs: clarify empty patches array behavior (#610)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-21 02:14:27 +00:00
afn
87e46f5a5a
chore(friendInvites): improve descriptions formatting (#628) 2023-03-21 03:13:11 +01:00
Nuckyz
e36f4e5b0a
Fixes and make guild tooltip show users inside hidden voice channels (#613)
* Fix #509

* Fix #597

* Fix #594
2023-03-19 22:03:33 -03:00
Xinto
4aff11421f
Replace update notices with notifications (#558) 2023-03-19 09:21:26 +00:00
Nuckyz
ea642d9e90
Fix #598 (#612) 2023-03-19 08:44:11 +00:00
fawn
17c3496542
feat(typingIndicator): Option to not show indicator for blocked users (#513) 2023-03-19 05:13:17 -03:00
Nuckyz
0fb79b763d
Improvements, changes and fixes (#611) 2023-03-19 04:53:00 -03:00
whqwert
5873bde6a6
fix(apiMessagePopover): fix match (#608) 2023-03-18 22:38:08 +01:00
Nuckyz
0b79387800
feat(PlatformIndicators): Colored mobile indicator option (#536)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-18 04:58:49 +01:00
Lewis Crichton
6b493bc7d9
feat(plugin): F8Break (#581)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-18 04:54:19 +01:00
LordElias
de53bc7991
messageLogger: fix edited timestamp styling & add i18n (#607) 2023-03-18 04:37:55 +01:00
Lewis Crichton
4c5a56a8a5
fix(RoleColorEverywhere): Chat mentions (#605) 2023-03-15 22:27:46 -03:00
Vendicated
ed873ef9de
Bump to v1.1.1 2023-03-15 18:01:04 +01:00
LordElias
d8a553feb0
improve MessageLogger deleted image hover animation (#603) 2023-03-14 18:48:36 +01:00
Ven
4717612090
:shipit: 2023-03-12 16:37:41 +01:00
Vendicated
5d1283bd85
Add Web/Desktop specific plugin capabilities; misc fixes 2023-03-11 14:18:32 +01:00
Ven
3b945b87b8
Delete src/plugins/reviewDB directory
Api owner refusing to properly moderate hate speech and related illegal / ToS infringing content
2023-03-11 12:26:54 +01:00
Vendicated
19c762f9c1
DevCompanion: Fix Deps 2023-03-11 00:28:27 +01:00
Vendicated
990adf7527
Fix casing in filename 2023-03-11 00:27:02 +01:00
Vendicated
983414d024
Add DevCompanion plugin (https://github.com/Vencord/Companion) 2023-03-11 00:25:49 +01:00
Vendicated
d5c05d857f
Add DevOnly plugin capability 2023-03-11 00:25:32 +01:00
Nuckyz
bff6788546
feat(plugins): SilentMessageToggle (#586)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-09 01:19:28 +01:00
Nuckyz
253183a16a
Fix Emote Cloner and improve ReverseImageSearch (#489) 2023-03-08 04:01:24 -03:00
Nuckyz
0fb3901a18
Fix Context Menu API (#583) 2023-03-08 06:01:15 +00:00
Nuckyz
1b199ec5d8
feat: Context Menu API (#496) 2023-03-08 01:59:50 -03:00
Nuckyz
40395d562a
Improvements for patches and misc stuff (#582) 2023-03-08 00:11:59 -03:00
Nuckyz
7322c3af04
Fix Crash Loops and prevent metrics (#580) 2023-03-06 22:54:01 +01:00
Nuckyz
36c27f1111
VCDoubleClick: Fix applying to non voice channels (#572) 2023-03-06 02:39:53 +01:00
Nuckyz
95db6c32a3
Fix Ignore Activities button on platforms different than Windows (#528)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-06 00:12:52 +01:00
Nuckyz
bed5e98bb0
Misc fixes and improvements (#555)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-05 22:49:59 +01:00
Nico
a5392e5c53
fix(silentTyping): fix chatbar icon patch (#570) 2023-03-05 22:30:37 +01:00
Sammy
abbd298b31
Fix(InvisibleChat) Fix chatbar icon patch (closes #560) (#566)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-05 22:05:46 +01:00
Nuckyz
e219aaa062
Notifications: Permanent option and close button (#563)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-04 18:49:15 +01:00
Vendicated
cab72e1be6
Strongly type useSettings (supersedes #559) 2023-03-04 18:41:32 +01:00
Berlin
92372bde1d
Update 1_INSTALLING.md (#562) 2023-03-03 23:55:21 +00:00
megumin
6747276a87
Add admin warnings to INSTALLING.md (#561) 2023-03-03 23:07:48 +00:00
Vendicated
03915b7533
Bump to v1.1.0 2023-03-02 21:19:33 +01:00
Vendicated
5e2ec368ad
patches: Make $self more robust 2023-03-02 21:17:15 +01:00
Vendicated
ab8c93fbac
Rewrite MessageLinkEmbeds part 2 2023-03-02 21:05:09 +01:00
Vendicated
d6a3edefd9
Rewrite MessageLinkEmbeds to improve Code Quality 2023-03-02 21:01:31 +01:00
Vendicated
727297ec4e
Fix messageLinkEmbeds 2023-03-02 18:49:24 +01:00
megumin
eccc4b0be1
feat(plugins): add FixInbox plugin (#552) 2023-03-02 04:55:30 +00:00
Vendicated
8465140bc4
Bump to v1.0.9 2023-03-01 21:40:31 +01:00
Lewis Crichton
e6ccb751a0
Fix for latest Discord Update (#550)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-03-01 21:35:08 +01:00
Marvin Witt
dfc7a15083
chore: extend description of NoDevtoolsWarning plugin (#545)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-01 18:32:58 +01:00
Vendicated
37003edae9
fix(Notifications): Correctly close errored notifications 2023-03-01 05:45:17 +01:00
Nuckyz
faa90eccd3
feat: Crash Handler (#531)
Co-authored-by: Ven <vendicated@riseup.net>
2023-03-01 05:26:13 +01:00
Cloudburst
c91b0df607
GMPolyfill: add header prop (#543) 2023-02-28 23:13:49 +01:00
Ven
f56d99e133
Update README.md 2023-02-28 22:38:02 +01:00
Ven
c690662802
Improve README 2023-02-28 22:37:09 +01:00
Vendicated
4918d699d5
Windows: Add Option to use native titlebar ~ Closes #537 2023-02-28 22:17:39 +01:00
Justice Almanzar
5ec517875e
typings for defaultless settings (#512)
* typings for defaultless settings

* fix other silly typings

* type guard utils

---------

Co-authored-by: Ven <vendicated@riseup.net>
2023-02-28 06:12:35 +01:00
Vendicated
cf56ad985b
oop oop oop 2023-02-28 02:43:58 +01:00
Vendicated
c09d1558f7
Add SupportHelper plugin 2023-02-28 02:40:45 +01:00
Vendicated
eb190b660e
Bump to v1.0.8 2023-02-28 01:50:17 +01:00
Lewis Crichton
d6f9068695
feat: SearchableSelect (#518)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-28 00:48:58 +01:00
Nico
cb507babaa
fix: vcDoubleClick and revealAllSpoilers patch (#517)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-28 00:41:14 +01:00
Vendicated
235d114193
Improve ConsoleShortcuts plugin 2023-02-28 00:38:28 +01:00
Vendicated
9aba70dcb1
Fix MenuItemDeobfuscator 2023-02-28 00:17:39 +01:00
Vendicated
0b61d29c31
Fix TypingTweaks 2023-02-28 00:17:28 +01:00
megumin
335a13a38a
fix tooltip component check (#541) 2023-02-27 21:19:01 +00:00
Vendicated
128ee41252
ErrorBoundary: Do not use any Discord components to be more robust 2023-02-25 19:10:01 +01:00
Vendicated
ccca41a168
Bump to v1.0.7 2023-02-24 06:08:45 +01:00
Vendicated
af4c7d8a90
Fix Cards (they look ugly now, wtf Discord) 2023-02-24 05:48:37 +01:00
nick
77c691651e
ReviewDB: Show edit instead of create review where applicable (#466)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-18 03:35:51 +01:00
Nuckyz
e14ec96e21
feat(FakeNitro): Bypass client themes and fixes (#504)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-18 03:32:02 +01:00
Vendicated
ff1f337699
Fix QuickCSS on electron 20+ 2023-02-17 15:37:38 +01:00
Nuckyz
3ca87848e5
TypingIndicator: Fix a dumb (#503)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-17 01:31:55 +01:00
Vendicated
9420735bc7
Version 1.0.6 2023-02-16 23:40:38 +01:00
Vendicated
6807820f6c
Badges should use ErrorBoundaries 2023-02-16 22:46:51 +01:00
Vendicated
3cad0d60b4
Silly Discord changed a bunch of css vars 2023-02-16 22:40:19 +01:00
Vendicated
fbbc198b1b
Fix PlatformIndicator 2023-02-16 22:31:13 +01:00
Nuckyz
224ae979f2
feat(plugins): Typing Indicator (#502) 2023-02-16 03:57:57 +01:00
Lewis Crichton
27fc20118b
feat(plugin): RoleColorEverywhere (#482)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-16 02:50:42 +01:00
Nuckyz
60ccd8cc25
Various plugin fixes (#492)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-16 02:00:09 +01:00
Lewis Crichton
5c1519156b
feat(plugin): ColorSighted (#501) 2023-02-16 01:46:14 +01:00
Vendicated
58270ef925
bump to v1.0.5 2023-02-14 19:22:01 +01:00
Vendicated
68055977d2
NotificationAPI: Correctly request browser permissions 2023-02-14 19:20:10 +01:00
Sammy
2b0c25b45c
Feat(InvisibleChat): Add Autodecryption (#490)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-12 22:10:03 +01:00
Vendicated
c154965d70
TypingTweaks: Fix crash after changing language 2023-02-12 21:07:05 +01:00
Ven
614234ad20
MessageLinkEmbeds: Prevent infinite cycles (#488) 2023-02-12 19:43:57 +01:00
Nuckyz
2489bc6831
Fix WhoReacted (#487)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-12 18:58:44 +01:00
fawn
d95be1acba
refactor: update plugins to use $self (#478)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-10 22:41:49 +01:00
Ven
1d995e58f5
Notification API (#467)
Co-authored-by: Ven <vendicated@riseup.net>
Co-authored-by: afn <hey@afn.lol>
Co-authored-by: afn <afnzmn@gmail.com>
2023-02-10 22:33:34 +01:00
Justice Almanzar
6114bc6b16
make proxies enumerable (#476)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-09 21:21:14 +01:00
Vendicated
ae98401bd3
Fix lag when alt tabbing to Discord 2023-02-09 19:36:30 +01:00
Nuckyz
992a77e76c
ShowHiddenChannels: Stage and voice channels support (#469)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-08 21:54:11 +01:00
Nuckyz
291f38115c
New webpack filter: byDisplayName (#474) 2023-02-08 21:48:26 +01:00
cryptofyre
8a52189378
feat(plugin): richerCider (#471)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-08 21:48:12 +01:00
Vendicated
70278f64a9
Fix broken patches 2023-02-01 18:00:25 +01:00
Vendicated
7b1d03699d
ci(reporter): Ignore 404/429 errors 2023-02-01 14:13:55 +01:00
Nico
8b40760187
fix(showHiddenChannels): revert lock icon to correct path (#465) 2023-02-01 13:59:58 +01:00
whqwert
de0990434e
feat(plugin): RevealAllSpoilers (#381)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-01 13:38:02 +01:00
Nuckyz
369d179bbf
ShowHiddenChannels: New screen for showing hidden channels (#460)
Co-authored-by: Ven <vendicated@riseup.net>
2023-02-01 12:11:05 +01:00
Nick
8f4e8d0a9b
TypingTweaks: fix crash on non en-US locales (#463) 2023-01-31 06:35:52 +01:00
Vendicated
62f7e4d45c
Add stylelint 2023-01-30 05:04:06 +01:00
Vendicated
fce7d6b681
Make webpack types importable from @webpack/types 2023-01-30 04:53:28 +01:00
Vendicated
69715070b9
browser ext: change applications to browser_specific_settings 2023-01-29 00:22:11 +01:00
Vendicated
d9fb7f45b5
ci: fix extension publishing 2023-01-29 00:19:39 +01:00
Vendicated
e32388e3ac
ci: fix version check 2023-01-29 00:12:27 +01:00
Vendicated
823fa2d0c3
Bump to v1.0.4 2023-01-29 00:10:17 +01:00
Vendicated
3cdffe444e
chore: Fix inconsistent file name casing 2023-01-29 00:09:17 +01:00
Nick
429ab9d363
feat(plugin): TypingTweaks (#432)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-29 00:06:33 +01:00
Kaydax
072ad3d7e6
feat(settings): Add the ability to make the window transparent (#457) 2023-01-28 23:54:38 +01:00
Sofia
6e22a96d9e
feat(ShowHiddenChannels): fix channel switch keybinds jumping to hiddens (#459)
Co-authored-by: Nuckyz
2023-01-28 01:40:10 +01:00
Ven
bc4c7473e8
ci: fix typo 2023-01-26 22:51:30 +01:00
Ven
399305fd8a
Automatic extension publishing (#453) 2023-01-26 22:38:02 +01:00
Vendicated
0c030a3a27
VcNarrator: Show all voices, better defaults 2023-01-25 21:08:45 +01:00
Vendicated
49aacccc19
shc: Make topic not inline 2023-01-25 21:06:22 +01:00
Vendicated
6ab4b48b47
chore: bump deps 2023-01-25 21:05:35 +01:00
Vendicated
103cd14361
Fix Themes Tab 2023-01-25 17:49:19 +01:00
Vendicated
41226f0358
Fix ShowHiddenChannels 2023-01-25 04:08:37 +01:00
Ven
5d3148cf50
New plugin: VcNarrator (#402)
Co-authored-by: Nico <nico@d3sox.me>
2023-01-25 03:42:01 +01:00
Nuckyz
d628924b59
ShowHiddenChannels: More improvements (#454)
- Remove buttons like the invite button when hovering hidden channels (as they do not work correctly)
- Make hideUnreads false work with HiddenIconWithMutedStyle
- migrate to definePluginSettings
- Change hardcoded constants to webpack gathering
- Clean up some patches
- Other minor things
- Make all patches use lookbehind for cleaner replacements (and better performance too lmao)
- Handle trying to connect to hidden channels
2023-01-25 03:35:34 +01:00
Ven
f19504f828
split up webpack commons into categories & type everything (#455) 2023-01-25 03:25:29 +01:00
Vendicated
a38ac956df
chore: Remove legacy workarounds 2023-01-24 13:50:02 +01:00
Vendicated
34276301c3
Fix Settings UI (Discord removed default margins 2023-01-24 13:35:57 +01:00
Ven
b2ecb02335
Make Windows Ctrl+Q feature optional; add opt-in auto update (#451) 2023-01-24 01:42:57 +01:00
Vendicated
25d32ce292
Settings: Fix plugin switch state not updating (fixes #209) 2023-01-23 22:43:25 +01:00
Dominik
cb4c50842f
[SpotifyControls] Add option to show Controls on hover (#352)
Co-authored-by: Dominik <domi@bambus.me>
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-23 22:25:00 +01:00
megumin
83757b19be
fix: emojis with duplicate names failing to clone (#449) 2023-01-23 21:11:28 +00:00
Nuckyz
75050e74ca
ShowHiddenChannels: better ui, alternative display mode (#446)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-23 22:04:50 +01:00
Vendicated
8a43e9b25f
dev: Better errors when using Node < v18 2023-01-23 04:02:09 +01:00
hunter
84cfe531af
Ignore dotfiles in plugin dirs (#447) 2023-01-23 00:27:55 +01:00
Vendicated
68e80c4d4c
Fix small QuickCss bug 2023-01-22 04:29:58 +01:00
Vendicated
b4f98e5066
Fix Settings ContextMenu Shortcuts & Settings on canary 2023-01-22 04:26:33 +01:00
Nuckyz
9602f527d8
Future proof Volume Booster to work with volume settings syncing (#439) 2023-01-21 14:41:10 +01:00
Nuckyz
64180362fd
ViewIcons: Fix finding ImageModal and props passing to MaskedLink (#442)
* Fix finding ImageModal and props passing to MaskedLink

* gonna stick this here
2023-01-21 14:37:36 +01:00
Nuckyz
6e44b8c47e
Fix Message Accessories API (#441) 2023-01-21 01:47:24 +01:00
Vendicated
2641adb29b
canary bad 2023-01-19 19:39:33 +01:00
Vendicated
ef5b3e1818
add issue title templates 2023-01-19 19:35:53 +01:00
Vendicated
7fe3a2c805
Add issue templates 2023-01-19 19:31:14 +01:00
Nuckyz
c4d2b4a8cd
Fix message logger patch (#431) 2023-01-19 00:36:31 +01:00
The Captain
08a2030bbc
feat(Plugin): customRPC (#406)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-18 23:47:40 +01:00
Nuckyz
5fe0600d6c
Fix Message Popover API (#425) 2023-01-17 03:33:33 +01:00
Ven
ebdcbcaf0c
Update README.md 2023-01-16 05:19:04 +01:00
Ven
1d287357ca
Reimplement Discord's Switch to fix performance (#413) 2023-01-15 21:26:02 +00:00
megumin
e49151ff33
remove power user instructions from readme (#417)
normal users will stop asking dumb questions in support!! pog
2023-01-15 04:59:20 +00:00
Vendicated
7478e880a8
ShowHiddenChannels: Use Lock as ChannelIcon 2023-01-14 23:01:19 +01:00
Nico
be7fa0cb3f
fix(showHiddenChannels): remove obsolete icons patch (#416)
resolves https://github.com/Vendicated/Vencord/issues/415
2023-01-14 20:32:33 +01:00
Sofia
9338b92b1a
feat(SilentTyping): add toggle command and icon (#398)
Co-authored-by: Ven <vendicated@riseup.net>
Co-authored-by: Justice Almanzar <superdash993@gmail.com>
2023-01-14 18:47:12 +01:00
Ven
efb0ef8b9c
Dev inject: add Mac support (#414) 2023-01-14 18:44:02 +01:00
Vendicated
fd766bc98f
Dev: Hot reload core css 2023-01-14 02:15:17 +01:00
Swishilicous
0e5b8b07c9
make plugin cards prettier (#389)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-14 00:25:24 +01:00
Jeroen Claassens
7582feb603
feat(messageActions): make features toggleable (#373)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-13 23:59:31 +01:00
Dominik
6329499b1d
git updater: Fix macOS (#391)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-13 23:24:51 +01:00
Nico
32cdb63885
fix(vcDoubleClick): fix functionality (#410)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-13 23:18:12 +01:00
Justice Almanzar
ea748dfb60
feat: Typesafe Settings Definitions (#403)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-13 23:15:45 +01:00
Ven
6c5fcc4119
Use GUI installer for pnpm inject/uninject (#407)
* Use GUI installer for pnpm inject/uninject

* Run Installer in DevMode
2023-01-13 17:52:28 +01:00
Vendicated
26f2b51eb9
Fix Frameless 2023-01-13 01:50:58 +01:00
Ven
075b0e0970
Update patcher.ts 2023-01-13 01:34:06 +01:00
Nick
10fd51071e
feat: Add option to disable the window frame (#400)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-12 23:48:37 +01:00
Ven
e70abc57b6
Update Windows Update patcher (#404) 2023-01-12 23:15:38 +01:00
Vendicated
a8678db78c
Fix React DevTools 2023-01-12 04:44:00 +01:00
Nick
bedb7b212b
feat(Plugin): Add AlwaysTrust (#401)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-12 02:55:02 +01:00
Nuckyz
b39cbcd934
fix(IgnoreActivities): Fix for upcoming change (#399) 2023-01-12 02:50:31 +01:00
Vendicated
19c9a13273
Fix InvisibleChat button getting hidden by themes 2023-01-11 01:50:00 +01:00
Vendicated
c525672777
Fix BetterRoleDot crash 2023-01-11 01:24:55 +01:00
Vendicated
a772aa62f5
Fix PetPet & CorruptMp4s 2023-01-09 23:19:00 +01:00
ZerXDE "Till O
23a461c36d
oneko: Disable pointer events to not block below buttons (#395)
Updated version of oneko which disables pointer Events.

Co-authored-by: Ven <vendicated@riseup.net>
2023-01-09 16:53:33 +01:00
Vendicated
da2d317555
Unhardcode PluginSettings styles 2023-01-09 16:23:40 +01:00
Vendicated
95df164e44
PluginSettings: Try to improve performance 2023-01-09 15:57:02 +01:00
Vendicated
ae9fe7fcfd
Fix Ctrl+Q shortcut working when Discord minimised 2023-01-08 19:13:57 +01:00
Nuckyz
f0240ec345
chore(plugins): Fix IgnoreActivities & clean up other plugins (#384) 2023-01-08 02:15:22 +01:00
Vendicated
15aa2299c3
Add Ctrl+Q Exit shortcut on Windows 2023-01-07 22:53:41 +01:00
Sammy
06aa72c636
feat(Plugin): InvisibleChat (#349)
Co-authored-by: Ven <vendicated@riseup.net>
2023-01-07 22:52:55 +01:00
Dominik
1713450540
[PlatformIndicators] Fix Chat Badges in DMs (#367) 2023-01-07 21:31:45 +01:00
ActuallyTheSun
eecc555dac
Fix Badges & MessageLinkEmbeds (#383) 2023-01-07 17:17:18 +01:00
Vendicated
5a3fbbfb30
unscuff profiles 2023-01-07 03:49:43 +01:00
Vendicated
cc51f6e2d2
Fix blurNSFW on canary 2023-01-07 03:28:40 +01:00
Vendicated
8113ed3c8c
Fix canary 2023-01-07 03:19:37 +01:00
ActuallyTheSun
b8ed72286b
fix(ViewIcons): module not found (#382) 2023-01-05 16:03:44 +01:00
Ven
9c5a149fb1
Move Installer to Vencord org 2023-01-04 20:56:14 +01:00
Ven
cf2bf2b43a
oop 2023-01-04 01:17:08 +01:00
Elliott Tallis
e6f759eecd
Add ImgOps to ReverseImageSearch (#379) 2023-01-03 19:42:06 +01:00
Vendicated
933216fcd5
QoL(PluginSettings): auto focus search bar 2023-01-03 02:37:22 +01:00
Vendicated
bcbbc79365
Happy new year 2023-01-03 02:32:08 +01:00
A user
374531d10e
Extract inline styles to css (#370) 2023-01-03 02:30:54 +01:00
Ven
2e5d27b6b6
feat: Proper CSS api & css bundle (#269)
Co-authored-by: Vap0r1ze <superdash993@gmail.com>
2022-12-25 20:47:35 +01:00
Dominik
2172cae779
[PlatformIndicators] Add own Status (#350)
Co-authored-by: Dominik <domi@bambus.me>
Co-authored-by: HypedDomi <HypedDomi@users.noreply.github.com>
Co-authored-by: Ven <vendicated@riseup.net>
2022-12-23 04:16:17 +01:00
Ven
e740f55450
Fix Vencord 2022-12-23 03:46:39 +01:00
Nickyux
aff1b68d6b
Add a "NEW" Badge for New Plugins (V2)! (#234)
Co-authored-by: Ven <vendicated@riseup.net>
Co-authored-by: Justice Almanzar <superdash993@gmail.com>
Co-authored-by: ArjixWasTaken <53124886+ArjixWasTaken@users.noreply.github.com>
2022-12-23 03:17:19 +01:00
Nuckyz
074542f0b3
feat(plugins): NoScreensharePreview plugin (#358) 2022-12-23 03:00:59 +01:00
Vendicated
b0c41d556a
Improve treeshaking 2022-12-22 18:05:04 +01:00
Elliott Tallis
af0d34b155
pointy is a "contributor" (#359) 2022-12-22 17:42:54 +01:00
Ven
6dd705f951
Update build.yml 2022-12-22 17:25:57 +01:00
Ven
259f0284f0
Update build.yml 2022-12-21 22:43:54 +01:00
Ven
cb9eb1f772
i hate ci i hate ci 2022-12-21 21:12:28 +01:00
Ven
42b4eebca1
Update build.yml 2022-12-21 21:05:52 +01:00
Ven
a9ee0c7e50
Delete obsolete FUNDING.yml 2022-12-21 20:59:06 +01:00
Elliott Tallis
73b7f11d7a
Also push builds to https://github.com/Vencord/builds (#344)
Co-authored-by: Ven <vendicated@riseup.net>
2022-12-21 20:58:07 +01:00
ActuallyTheSun
d806be1346
feat(PlatformIndicators): add indicator to messages (#343) 2022-12-21 20:16:32 +01:00
V3L0C1T13S
1f73cfa91a
EmoteCloner: Use CDN_HOST variable to support unofficial backends (#356) 2022-12-21 16:12:05 +01:00
Nuckyz
7e6077367a
feat(plugins): DisableDMCallIdle (#355) 2022-12-20 23:54:47 +01:00
Vendicated
103c499310
Monaco Popup: Add metadata, store window instance 2022-12-20 18:04:33 +01:00
Vendicated
9dcafbf468
Fix Notices
Have I ever mentioned how terrible Discord's Notices code is?
2022-12-20 18:03:58 +01:00
Nuckyz
1742bb6020
Fix StartupTimings (#353) 2022-12-20 16:18:15 +01:00
Vendicated
0743c1215e
Add canary test 2022-12-20 02:59:16 +01:00
Vendicated
94ad8e8f61
Add useEffect/useState/useMemo to webpack commons 2022-12-20 00:34:26 +01:00
Justice Almanzar
989bd36eeb
refactor: identifier escapes + "self" group (#339)
Co-authored-by: Ven <vendicated@riseup.net>
2022-12-19 22:59:54 +00:00
Nuckyz
4974c53f9c
Improve PronounDB patch (#348) 2022-12-18 05:13:34 +01:00
Manti
47de9fab2e
Make some changes to reviewdb ui and add badges to it (#245) 2022-12-17 23:30:29 +01:00
Nico
3efc79224f
[ShowHiddenChannels] Fix last message date (#342) 2022-12-16 15:51:23 +01:00
Nuckyz
456164253d
fix(MessageLogger): Fix module not being found (#338) 2022-12-16 14:16:47 +01:00
Vendicated
c257f86576
Fix experiments 2022-12-15 17:53:12 +01:00
Pedro
f6122a00ca
feat(NoReplyMention): exempt list support (#337) 2022-12-15 14:05:44 +00:00
Ven
f1bdfdd6b9
Update nsfwGateBypass.ts 2022-12-14 23:50:00 +01:00
ActuallyTheSun
c8f2141114
feat(plugin): add MessageLinkEmbeds (#264)
Co-authored-by: Ven <vendicated@riseup.net>
2022-12-14 23:44:58 +01:00
megumin
fea8c60a40
hotfix injector for ptb/canary/dev (#330) 2022-12-14 23:35:02 +01:00
A user
a67db11dc2
Improve Settings UI & View Raw Modal (#332)
very cool
2022-12-14 00:44:57 +01:00
Box_
9a088b7a31
MoreKaomoji: Add more kaomoji (#299) 2022-12-09 22:54:46 +01:00
Justice Almanzar
ebb8da0f23
fix(FakeNitro): more reliable patches (#304) 2022-12-09 04:32:16 +01:00
Commandtechno
f2e0542614
New Plugin: NSFWGateBypass (#300) 2022-12-09 00:35:09 +01:00
megumin
ee24439795
feat(plugin): sort friend requests by date received (#280) 2022-12-08 23:53:12 +01:00
David Ralph
022bf17140
fix inconsistent margins & capitalisation (#281) 2022-12-08 23:51:18 +01:00
Justice Almanzar
2de461985d
fix(ShikiCodeblocks): spoilers (#298)
* fix(ShikiCodeblocks): spoilers

* fix a settings bug i thikn
2022-12-08 15:54:19 +01:00
Justice Almanzar
2d08dd8a9c
Shiki settings preview (#297) 2022-12-07 15:33:40 +01:00
Commandtechno
49b45d8262
google changed their shit (#294) 2022-12-05 23:14:48 +00:00
Cloudburst
8a5a5c7d1e
UserScript: add csp bypassing fetch (#284) 2022-12-04 13:58:29 +01:00
Nuckyz
53d0a55561
refactor(IgnoreActivities): Use React Components and support Embedded Activities (#282) 2022-12-04 02:16:47 +01:00
Commandtechno
25ef5d60b4
add me to contributors (#287) 2022-12-03 22:42:18 +00:00
Commandtechno
c74241fde6
add commas in member count (#286)
l
2022-12-03 23:11:08 +01:00
Vendicated
4d8145f12c
Fix arrpc 2022-12-03 14:58:00 +01:00
Ven
d4f70218ba
ci: Do not release extension-v2.zip 2022-12-03 13:42:46 +01:00
Ven
6b4b4772bb
Update README.md 2022-12-03 13:41:31 +01:00
Justice Almanzar
54010aab94
fix: hljs fallback (#283) 2022-12-03 11:32:14 +01:00
A user
62b2acebe6
Add support for Flatpak for Git updating (#274)
Co-authored-by: Ven <vendicated@riseup.net>
2022-12-02 16:55:53 +01:00
Justice Almanzar
41dddc9eee
feat(plugin): ShikiCodeblocks (#267)
Co-authored-by: ArjixWasTaken <53124886+ArjixWasTaken@users.noreply.github.com>
Co-authored-by: Ven <vendicated@riseup.net>
2022-12-02 16:43:37 +01:00
12944qwerty
4760af7f0e
add ViewRaw plugin & MiniPopover API (#275)
Co-authored-by: Vendicated <vendicated@riseup.net>
2022-12-02 16:38:52 +01:00
Vendicated
06d32ae414
browser: remove firefox extension id 2022-12-02 14:24:23 +01:00
Vendicated
2564ab73f5
ci: unlisted firefox builds for now 2022-12-02 14:21:44 +01:00
Vendicated
5e97cc0fc3
QuickCss: Hide MenuBar; explicitly enable contextIsolation
Closes #260
2022-12-02 14:11:20 +01:00
Vendicated
b9e9d9bd64
Add --vanilla flag, strip csp on mainFrame only 2022-12-02 14:10:40 +01:00
Vendicated
daf3a1dcac
Try to make firefox publish work 2022-12-01 19:43:57 +01:00
Ven
f1fb79d2c5
Fix workflow 2022-12-01 19:22:49 +01:00
Roman / Linnea Gräf
0ff6d3dd41
Add Firefox extension build (#277) 2022-12-01 19:16:09 +01:00
Vendicated
734054ff68
feat(Settings): Allow moving Vencord section to different places 2022-12-01 03:38:17 +01:00
Vendicated
f94cbfb2f4
Add basic themes tab 2022-12-01 03:01:44 +01:00
Sofia
fc09460d82
feat(plugin): add ServerListIndicators (#272) 2022-11-29 00:25:07 +01:00
Vendicated
e884738f42
MemberCount: Fix misleading count, add tooltip 2022-11-28 23:01:09 +01:00
megumin
c583bad6bf
the shiggy wiggy (#270) 2022-11-28 18:59:42 +00:00
Vendicated
36b787812e
Add MemberCount plugin 2022-11-28 19:29:46 +01:00
Ven
836ae72076
Delete report.md 2022-11-28 16:00:54 +01:00
Vendicated
d0a40bc0ed
chore: update deps 2022-11-28 15:59:15 +01:00
Vendicated
3b4879f9d9
perf(settings): Cache proxies 2022-11-28 15:44:53 +01:00
Vendicated
a0a1a4d139
enforce path aliases with eslint 2022-11-28 13:59:53 +01:00
Ven
bad96b7887
Path aliases, better lazyWebpack (#268) 2022-11-28 13:37:55 +01:00
Vendicated
7a4402f142
BlurNSFW: Support videos 2022-11-28 01:08:58 +01:00
Vendicated
3e9672c6b8
oop 2022-11-28 00:58:26 +01:00
Vendicated
a9fee6248e
BlurNSFW: Add amount setting 2022-11-28 00:55:50 +01:00
Vendicated
f0ee16f173
[skip ci] update genPluginList 2022-11-28 00:45:41 +01:00
Vendicated
3db3c63b42
BlurNsfw plugin 2022-11-28 00:42:42 +01:00
megumin
4fc41c8c0b
fix: add predicate to updater menu item (#266)
* fix: add predicate to updater menu item

* dont include Updater in web builds

* i can spell
2022-11-27 16:07:31 +01:00
Hana
47c181beec
[skip ci] Add support for DVM installation path. (#265) 2022-11-27 14:32:54 +01:00
megumin
c4fc01c7ff
[skip ci] feat(ci): test web builds (#262) 2022-11-25 23:51:36 +01:00
Ven
5a94201578
Megu blowing up main :blobcatcozyscared: 🚎 2022-11-25 23:41:02 +01:00
megumin
6b55dee9fb
feat(settings): new settings design (#261) 2022-11-25 22:38:55 +00:00
Vendicated
a85ec594a7
[skip ci] docs docs docs 2022-11-25 19:25:35 +01:00
Vendicated
c2c6c9fccb
CallTimer: Fix lag 2022-11-25 18:28:15 +01:00
Vendicated
b60f6cb18d
WhoReacted: Make more reliable & don't spam api 2022-11-25 18:07:29 +01:00
Vendicated
bb398970ef
HideAttachments: Fix embeds
Closes #259
2022-11-25 18:06:31 +01:00
Vendicated
50a96e8047
CallTimer: Fix typo 2022-11-25 16:16:07 +01:00
Vendicated
c5b5b754e2
CallTimer 2022-11-25 15:59:47 +01:00
KraXen72
0f644dff73
loadingQuotes quote fix (#255) 2022-11-24 14:26:38 +01:00
Snare-Hawk
6210d3a597
Make ReviewDB Look More Native (#256) 2022-11-24 14:26:18 +01:00
Nico
e7573382fe
fix(betterNotes): add restart needed for hide notes patch (#258) 2022-11-24 14:02:11 +01:00
Vendicated
f4d7a1f4fb
New Plugin: BetterNotesBox 2022-11-24 02:02:15 +01:00
Vendicated
5dd0a3a746
New Plugin: HideAttachments 2022-11-24 01:00:13 +01:00
Ven
c9fac8ffff
fix tags 2022-11-23 20:04:25 +01:00
KraXen72
f93607fc66
add new quotes to loadingQuotes (#254)
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-23 20:00:19 +01:00
Luna
63ffb5bebc
feat(messageTags): Add message quick reply plugin (#241) 2022-11-23 19:56:20 +01:00
jd
2788d264d4
feat(plugin): Urban Dictionary (#222) 2022-11-23 14:30:59 +01:00
Nuckyz
91f1d68e29
feat(plugins): Keep Current Channel plugin (#248) 2022-11-23 02:51:45 +01:00
Nuckyz
7e4f4f1794
feat(plugins): Volume Booster plugin (#249) 2022-11-22 23:22:54 +01:00
megumin
9f7ec0aa8d
settings: better button text for plugin settings modal (#251) 2022-11-22 22:05:46 +00:00
Vendicated
0239bb0aac
Commands: Show plugin name instead of 'Built-In' 2022-11-22 22:42:22 +01:00
Vendicated
ec20556d5c
PlatformIndicators: Fix icon colours 2022-11-22 17:06:24 +01:00
Ven
11191b5943
Update 1_INSTALLING.md 2022-11-21 22:26:12 +01:00
CanadaHonk
1f72a0fc27
fix(arRPC): fix error on null activity (#244) 2022-11-21 20:40:40 +01:00
megumin
31ec1ec1b4
better platformindicators settings (#243) 2022-11-21 20:12:46 +01:00
Nickyux
0f7c80fd4d
Fix no gap (#242) 2022-11-21 19:54:48 +01:00
Ven
b5bc88c7d4
Settings export/import (#235) 2022-11-21 19:25:40 +01:00
Kareem Olim
b42b8d755f
Platform indicators: ignore unnecessary element (#240) 2022-11-21 19:25:21 +01:00
megumin
bfe1fd9912
fix: add keys to plugins grid (#237) 2022-11-21 18:45:22 +01:00
Cloudburst
c45d89697a
make userscript autoincrement version :trollface: (#233) 2022-11-21 16:32:56 +01:00
Vendicated
0a92bd6521
PlatformIndicators: Fix server list 2022-11-21 15:59:19 +01:00
Kareem Olim
33c33eb0fd
feat(plugin): PlatformIndicators (#227)
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-21 15:44:30 +01:00
obscurity
dcf1148bb4
feat(plugin): TimeBarAllActivities (#228) 2022-11-21 11:53:28 +01:00
obscurity
58e28b4281
feat(fakeNitro): add an option to change emote sizes (#225)
closes https://github.com/Vendicated/Vencord/issues/204
2022-11-21 03:43:16 +01:00
Sofia
bb14d4989d
feat(plugin): NoUnblockToJump (#229) 2022-11-21 03:40:15 +01:00
CanadaHonk
9bcdc8451f
feat(arRPC): update for server 2.2 (#230) 2022-11-21 00:57:30 +01:00
Nuckyz
46b14cb2e0
feat(plugins):WhoReacted keep reaction count (#231) 2022-11-21 00:56:17 +01:00
CanadaHonk
9240865f65
feat(arRPC): update for server 2.0 (#224) 2022-11-20 16:21:42 +01:00
CanadaHonk
e85d763f22
feat(plugin): WebRichPresence (arRPC) (#223) 2022-11-20 14:31:00 +01:00
Vendicated
82911386db
oop 2022-11-19 22:17:55 +01:00
Vendicated
e63ed9cac4
onekocord 2022-11-19 22:13:16 +01:00
Sofia
ba45ecda56
feat(plugin): Last.fm rich presence (#220)
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-19 18:40:52 +01:00
megumin
7ff2d2ba8a
fix startup timings page (#219) 2022-11-19 15:52:17 +00:00
Kareem Olim
a5154d6283
feat(plugin): Quick mention button (#218)
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-19 16:11:11 +01:00
Kareem Olim
5ce2dc1bb4
feat(plugin): Read all notifications button (#217)
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-19 14:54:48 +01:00
Vendicated
8f2c247f27
Fix commands showing up multiple times Part 2 2022-11-18 23:31:53 +01:00
Vendicated
43f41d20fa
Fix commands showing up multiple times 2022-11-18 23:29:34 +01:00
Ven
50c356e397
fix brain fart 2022-11-18 13:39:43 +01:00
Berlin
503a2ec517
Add option to ignore incoming blocked messages (#179) 2022-11-18 05:12:45 +01:00
Vendicated
83b3b1f16b
fix settings debug info on web 2022-11-17 13:49:51 +01:00
Vendicated
2628bdce42
WebContextMenus: Port copy/open link items to Discord Web 2022-11-17 01:30:23 +01:00
Vendicated
8b0911b86a
Updater: Ignore non release commits 2022-11-17 00:45:00 +01:00
Ven
47d127a895
Update FUNDING.yml 2022-11-17 00:41:20 +01:00
Vendicated
410613726b
Donor Badges && Add donate info to settings 2022-11-17 00:21:20 +01:00
Ven
8b3f290e3c
Create FUNDING.yml 2022-11-16 22:40:26 +01:00
Vendicated
a788813383
VencordWeb: Migrate to manifest v3 2022-11-16 16:23:52 +01:00
Vendicated
e1de6f88fe
Unexplode Modals on canary 2022-11-16 14:52:05 +01:00
Vendicated
ae86848cf6
Fix ReviewDB 2022-11-16 01:02:23 +01:00
Manti
84ec839b04
Add ReviewDB Plugin (#187)
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-16 00:40:46 +01:00
Vendicated
b30508aef8
better handling for settings ui errors 2022-11-15 17:29:31 +01:00
Nico
eabbf7d9bd
fix(fakeNitro): add missing predicate for sticker bypass (#215) 2022-11-15 09:34:53 +01:00
Vendicated
be088f9072
Don't unnecessarily create functions many times 2022-11-15 09:30:33 +01:00
Vendicated
2ca98a87d2
Fix Settings UI on canary 2022-11-15 09:28:06 +01:00
Vendicated
b49ac6b541
ClickableRoleDot -> BetterRoleDot; now allows using both role colour styles at once 2022-11-14 21:42:02 +01:00
Ven
82e444e196
Less confusing plugin names (bulk plugin rename) (#214)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2022-11-14 18:05:41 +01:00
Ven
a96f8a89f3
MessageLogger: fixes + ignoreSelf & ignoreBots option (#213) 2022-11-14 16:22:50 +01:00
afn
4642b54260
feat(plugin): FriendInvites (#208)
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-13 23:34:10 +01:00
Ven
15b257a7b0
Friendlier Readme 2022-11-13 23:24:25 +01:00
rushii
0dbec8d0cd
feat: message logger plugin (#49)
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-13 23:13:32 +01:00
Vendicated
e5b23ff556
EmoteYoink: Don't depend on ReverseImageSearch whoopsie 2022-11-13 04:12:37 +01:00
Vendicated
9110d1f9bd
Emote Clone plugin 2022-11-13 03:46:46 +01:00
Vendicated
81edc14070
fix PronounDB crash with new profile in dms, force start dependencies 2022-11-12 17:20:19 +01:00
Vendicated
b48c8d8a4a
NitroSpoof: Fix inbuilt sticker logic; cleanup 2022-11-12 16:25:28 +01:00
Vendicated
8380328465
InteractionKeybinds: Scroll to message if offscreen
Closes https://github.com/Vendicated/Vencord/issues/200
2022-11-12 00:29:36 +01:00
megumin
30ca4f1cf9
feat: Badge API (#206) 2022-11-11 23:50:09 +01:00
Nuckyz
62e0787cf2
fix(plugins): Fix IgnoreActivities (#205) 2022-11-11 19:02:03 +01:00
Vendicated
cc7c14ec88
Reporting: do not error for patches with all:true 2022-11-11 19:01:01 +01:00
Vendicated
a86452e774
fix react hook error 2022-11-11 18:58:30 +01:00
Vendicated
dddb28192c
even more plugin fixes 2022-11-11 18:49:47 +01:00
Vendicated
2133823bd3
more plugin fixes 2022-11-11 16:43:40 +01:00
Vendicated
1176896a1b
fix(plugins): PronounDB, ViewIcons, WebhookTags, NoBlockedMessages, BetterGifAltText, MessageAccessories 2022-11-11 16:14:09 +01:00
Ven
f3aba3edb0
ci: Add webhook secret to reporter env 2022-11-11 13:30:51 +01:00
Vendicated
409e54a9d8
ci(reporter): Post results to discord webhook 2022-11-11 13:27:44 +01:00
Vendicated
31fb19b8c9
ci: Hopefully fix reporting 2022-11-11 13:06:04 +01:00
Vendicated
a26f636c9b
ci: Automated plugin test with puppeteer 2022-11-11 12:37:37 +01:00
Vendicated
8ba9c96f20
Fix most plugins 2022-11-11 00:11:44 +01:00
Jānis
57f3feba68
spotifyControls: make album of local tracks unclickable (#203) 2022-11-10 19:33:00 +01:00
megumin
010523eeac
feat(plugins): add vc effect event to moyai plugin (#199) 2022-11-10 14:04:06 +01:00
Nico
15f12073cf
spotifyControls: make title/artists of local tracks unclickable (#201)
Co-authored-by: Vendicated <vendicated@riseup.net>
2022-11-10 14:02:34 +01:00
Vendicated
58636a9a82
CorruptMp4s: Depend on CommandsAPI ~ PronounDB: Add pronoundb link 2022-11-09 23:01:59 +01:00
Vendicated
0bc894d065
CorruptMp4s: Better default 2022-11-09 21:17:21 +01:00
Vendicated
6f38c4b7fe
new plugin(CorruptMp4s): Mp4s with infinite/negative duration 2022-11-09 21:15:52 +01:00
Vendicated
c1d2f0078f
StickerSpoof: Fix not correctly cleaning previous frame 2022-11-09 20:29:35 +01:00
Vendicated
3c8084ec36
Add VSCode debug config 2022-11-09 19:26:46 +01:00
Nico
3b65384b94
fix(spotifyControls): add album/cover null checks (local files) (#198)
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-09 17:36:20 +01:00
Vendicated
e0450531ef
StickerSpoof: fix small resolutions; AnonymiseFiles: fix extension logic 2022-11-09 17:30:48 +01:00
Ven
b032e9b6e3
oop 2022-11-09 16:08:11 +01:00
Vendicated
1e6b967d24
Fix moyai and fart 2022-11-09 12:47:16 +01:00
Vendicated
460f329e4f
fix double click actions using outdated content 2022-11-08 18:09:11 +01:00
Nuckyz
3a3a52c493
fix(NitroBypass): Fix using stickers bypass with Nitro Classic (#196) 2022-11-08 17:51:09 +01:00
afn
4e57ae66f1
feat(SpotifyControls): prettier design (#194)
Co-authored-by: afn <hey@afn.lol>
Co-authored-by: KraXen72 <DPELECH1@GMAil.com>
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-08 17:31:36 +01:00
Vendicated
f7d9be9140
lint: Disallow utils index imports
This keeps leading to issues due to circular imports.
Import from specific files instead, index just reexports
2022-11-07 23:34:14 +01:00
Vendicated
955573d31b
me when i dont depend on MenuItemDeobfuscatorApi 2022-11-07 22:36:06 +01:00
Ven
6a8564089b
SpotifyControls plugin (#190) 2022-11-07 22:28:29 +01:00
Nico
7d5ade21fc
feat(nitroBypass): add sticker bypass (#184)
Co-authored-by: Vendicated <vendicated@riseup.net>
2022-11-07 22:23:34 +01:00
Ven
d69dfd6205
ahem 2022-11-07 21:39:49 +01:00
Ven
177d353f50
Rename DevBuild releases to include git hash 2022-11-07 21:38:14 +01:00
Vendicated
a13c0df1cd
build: Add metadata header to all bundles 2022-11-07 21:29:31 +01:00
Vendicated
0af4579204
Add tracer, fix MessageActions slow startup 2022-11-07 21:05:33 +01:00
Vendicated
851d07f31a
fix(ReverseImageSearch): Don't apply to non image files 2022-11-07 18:52:34 +01:00
Vendicated
963a7332b4
Migrate proxied components to and fix LazyComponent 2022-11-06 18:37:01 +01:00
Vendicated
440baf6028
Improve proxyLazy 2022-11-06 18:00:59 +01:00
megumin
9663e229a6
feat(plugins): add Startup Timings (#189) 2022-11-05 11:09:05 +01:00
megumin
0cb24cad7e
feat: make text selectable in PatchHelper (#188)
* feat: make text selectable in PatchHelper

* real div moment
2022-11-05 10:02:29 +01:00
Vendicated
65620f4976
Webpack: Do not emit errors if devtools open 2022-11-03 20:36:17 +01:00
Vendicated
cb7469afad
Simplemarkdown pleeeeease 2022-11-03 19:15:51 +01:00
Vendicated
2c3dee4120
qol improvements 2022-11-03 19:12:50 +01:00
Vendicated
c20dc269d2
Modify CSP instead of deleting it 2022-11-02 22:15:55 +01:00
Vendicated
a7795533df
Remove clipboardImageFix - Discord fixed the bug woooooo 2022-11-02 20:54:39 +01:00
Vendicated
5e1b42120c
Fix plugins on new update 2022-11-02 20:13:55 +01:00
Vendicated
676f5c7e30
ViewIcons: size 2048 -> 512, to fit on screen 2022-11-02 17:30:15 +01:00
Vendicated
13c73699e9
Fix Webpack modules that are not arrow funcs, Part II 2022-11-01 15:06:15 +01:00
Vendicated
64aed87de4
Fix Webpack modules that are not arrow funcs 2022-11-01 14:28:25 +01:00
Nickyux
1944f3957f
fix forceOwnerCrown Plugin Spamming Errors in Console (#180)
Co-authored-by: Nico <nico@d3sox.me>
Co-authored-by: Ven <vendicated@riseup.net>
2022-11-01 02:19:07 +01:00
Ven
04d6f341ee
PatchHelper, a tool to help you write patches (#182) 2022-11-01 01:49:41 +01:00
rushii
0c25278c59
fix renamed app.asar detection on Windows (#185) 2022-11-01 01:47:07 +01:00
Vendicated
0fda900ccc
Fix: settings.appearance may be undefined 2022-10-31 17:17:54 +01:00
Ven
8adf7ca155
Webpack Warnings & Errors (#178)
* dev: Useful strict Warnings & Errors

* Always log error

* Ignore pending patches with all or whose predicate = false

* Error -> Warn
2022-10-30 20:45:18 +01:00
Snek
b905743077
removed channel type (#170)
Co-authored-by: Ven <vendicated@riseup.net>
2022-10-30 19:17:46 +01:00
Nico
a43a41f61f
vcDoubleClick: don't require dbl click on active vc, fix stage channels (#172) 2022-10-30 18:47:12 +01:00
Ven
3af9a14a0e
Patcher: More useful errors with code diffs (#177)
* Patcher: More useful errors with code diffs

* Nicer log formatting

* PluginCards: ellipsises
2022-10-30 02:58:11 +01:00
Vendicated
739b1e47d4
New plugin: LoadingQuotes 2022-10-29 22:53:23 +02:00
Ven
d72542405a
Implement Subcommands; fix errors due to Settings <-> Plugins circular imports (#174) 2022-10-29 20:45:31 +02:00
Vendicated
95aa2d9d8d
ClipboardImageFix is not actually required 2022-10-29 20:36:43 +02:00
Ven
93859883c1
build: inject createElement alias (#176) 2022-10-29 20:27:48 +02:00
Cynthia Foxwell
37105ac416
feat(plugin): ClipboardImageFix (#173) 2022-10-29 20:25:40 +02:00
Vendicated
f6e0efe20a
Reverse image search plugin 2022-10-29 15:25:34 +02:00
Vendicated
1764206e19
Add MenuItemDeobfuscator 2022-10-29 15:25:10 +02:00
Nuckyz
6b0caaae37
fix(ShowHiddenChannels): Fix New unreads box for hidden channels #168 2022-10-27 20:26:54 +02:00
Snek
c76e9f5e3d
better patch & visual bug fix (#167) 2022-10-27 18:37:54 +02:00
Ven
ce73a5f172
use gh cli to update release (#166) 2022-10-27 10:55:56 +02:00
Nuckyz
9548978d80
fix(IgnoreActivities): id -> exePath (#164)
Co-authored-by: Ven <vendicated@riseup.net>
2022-10-27 10:27:52 +02:00
megumin
13882b5732
feat: custom components in settings (#165) 2022-10-26 23:42:26 +02:00
Jakup
49e72bab32
moarKaomojis plugin (#137) 2022-10-26 15:38:41 +02:00
Nuckyz
bbd3633038
fix(Ignore Activities): Fixes games manually added not being able to be ignored (#162)
Co-authored-by: Ven <vendicated@riseup.net>
2022-10-26 15:31:55 +02:00
Vendicated
c65f757bc4
Fix betterUploadButton 2022-10-26 15:10:11 +02:00
Vendicated
b87f0bf3f9
Settings: Cache default value 2022-10-26 14:28:27 +02:00
Vendicated
670b7d7d01
Make tsc happy 2022-10-26 13:54:23 +02:00
Vendicated
f492d26379
Make jsFactory shorter -> bundle size -10% 2022-10-26 13:49:28 +02:00
Ven
56b00f715a
ci: Only rebuild on actual code changes 2022-10-26 13:03:53 +02:00
Vendicated
fe5a78ddc9
Update README 2022-10-25 21:40:23 +02:00
megumin
5e7c155f6e
feat(settings): add beforeSave check (#161) 2022-10-25 18:49:50 +01:00
Nico
e06ba68c40
fix(vcDoubleClick): add required parentheses (#160) 2022-10-25 18:15:21 +02:00
Nico
d6fe937a70
fix(vcDoubleClick): exclude text channel mentions (#159) 2022-10-25 18:09:21 +02:00
Nico
2f46b934c9
feat: add new plugin ForceOwnerCrown (#157) 2022-10-25 17:32:05 +02:00
Vendicated
2af324c302
Improve genPluginList 2022-10-25 12:29:55 +02:00
Vendicated
4bddcee40b
Add autogenerated plugin list, closes #151 2022-10-25 12:20:29 +02:00
Nico
559edbfffe
Fix vcDoubleClick, add support for stage channels (#158) 2022-10-25 10:53:06 +02:00
Nuckyz
6c38362401
Ignore Activities: Fix button not working (#156)
explode
2022-10-25 00:18:30 +02:00
Nuckyz
00402c69d6
feat(plugin): Ignore Activities (#142) 2022-10-25 00:05:40 +02:00
Nico
30dd4b9e01
[ShowHiddenChannels] Add last message info, fix collapsing (#146)
Co-authored-by: Snek <107999380+sneksnake@users.noreply.github.com>
2022-10-24 23:22:39 +02:00
461 changed files with 39207 additions and 5000 deletions

@ -2,40 +2,36 @@
"root": true,
"parser": "@typescript-eslint/parser",
"ignorePatterns": ["dist", "browser"],
"plugins": ["header", "simple-import-sort", "unused-imports"],
"plugins": [
"@typescript-eslint",
"simple-header",
"simple-import-sort",
"unused-imports",
"path-alias"
],
"settings": {
"import/resolver": {
"alias": {
"map": [
["@webpack", "./src/webpack"],
["@webpack/common", "./src/webpack/common"],
["@utils", "./src/utils"],
["@api", "./src/api"],
["@components", "./src/components"]
]
}
}
},
"rules": {
// Since it's only been a month and Vencord has already been stolen
// by random skids who rebranded it to "AlphaCord" and erased all license
// information
"header/header": [
2,
"block",
[
{
"pattern": "!?",
"template": " "
},
" * Vencord, a modification for Discord's desktop app",
{
"pattern": " \\* Copyright \\(c\\) \\d{4}",
"template": " * Copyright (c) 2022 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/>.",
""
],
2
"simple-header/header": [
"error",
{
"files": ["scripts/header-new.txt", "scripts/header-old.txt"],
"templates": { "author": [".*", "Vendicated and contributors"] }
}
],
"quotes": ["error", "double", { "avoidEscape": true }],
"jsx-quotes": ["error", "prefer-double"],
@ -43,7 +39,7 @@
"indent": ["error", 4, { "SwitchCase": 1 }],
"arrow-parens": ["error", "as-needed"],
"eol-last": ["error", "always"],
"func-call-spacing": ["error", "never"],
"@typescript-eslint/func-call-spacing": ["error", "never"],
"no-multi-spaces": "error",
"no-trailing-spaces": "error",
"no-whitespace-before-property": "error",
@ -55,7 +51,10 @@
"eqeqeq": ["error", "always", { "null": "ignore" }],
"spaced-comment": ["error", "always", { "markers": ["!"] }],
"yoda": "error",
"prefer-destructuring": ["error", { "object": true, "array": false }],
"prefer-destructuring": ["error", {
"VariableDeclarator": { "array": false, "object": true },
"AssignmentExpression": { "array": false, "object": false }
}],
"operator-assignment": ["error", "always"],
"no-useless-computed-key": "error",
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
@ -63,9 +62,13 @@
"no-constant-condition": ["error", { "checkLoops": false }],
"no-duplicate-imports": "error",
"no-extra-semi": "error",
"consistent-return": ["warn", { "treatUndefinedAsUnspecified": true }],
"dot-notation": "error",
"no-useless-escape": "error",
"no-useless-escape": [
"error",
{
"extra": "i"
}
],
"no-fallthrough": "error",
"for-direction": "error",
"no-async-promise-executor": "error",
@ -88,6 +91,8 @@
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"unused-imports/no-unused-imports": "error"
"unused-imports/no-unused-imports": "error",
"path-alias/no-relative": "error"
}
}

1
.gitattributes vendored Normal file

@ -0,0 +1 @@
* text=auto eol=lf

30
.github/ISSUE_TEMPLATE/blank.yml vendored Normal file

@ -0,0 +1,30 @@
name: Blank Issue
description: Create a blank issue. ALWAYS FIRST USE OUR SUPPORT CHANNEL! ONLY USE THIS FORM IF YOU ARE A CONTRIBUTOR OR WERE TOLD TO DO SO IN THE SUPPORT CHANNEL.
body:
- type: markdown
attributes:
value: |
# READ THIS BEFORE OPENING AN ISSUE
This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS.
DO NOT USE THIS FORM, unless
- you are a vencord contributor
- you were given explicit permission to use this form by a moderator in our support server
- you are filing a security related report
- type: textarea
id: content
attributes:
label: Content
validations:
required: true
- type: checkboxes
id: agreement-check
attributes:
label: Request Agreement
options:
- label: I have read the requirements for opening an issue above
required: true

81
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file

@ -0,0 +1,81 @@
name: Bug/Crash Report
description: Create a bug or crash report for Vencord. ALWAYS FIRST USE OUR SUPPORT CHANNEL! ONLY USE THIS FORM IF YOU ARE A CONTRIBUTOR OR WERE TOLD TO DO SO IN THE SUPPORT CHANNEL.
labels: [bug]
title: "[Bug] <title>"
body:
- type: markdown
attributes:
value: |
# READ THIS BEFORE OPENING AN ISSUE
This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS.
DO NOT USE THIS FORM, unless
- you are a vencord contributor
- you were given explicit permission to use this form by a moderator in our support server
DO NOT USE THIS FORM FOR SECURITY RELATED ISSUES. [CREATE A SECURITY ADVISORY INSTEAD.](https://github.com/Vendicated/Vencord/security/advisories/new)
- type: input
id: discord
attributes:
label: Discord Account
description: Who on Discord is making this request? Not required but encouraged for easier follow-up
placeholder: username#0000
validations:
required: false
- type: textarea
id: bug-description
attributes:
label: What happens when the bug or crash occurs?
description: Where does this bug or crash occur, when does it occur, etc.
placeholder: The bug/crash happens sometimes when I do ..., causing this to not work/the app to crash. I think it happens because of ...
validations:
required: true
- type: textarea
id: expected-behaviour
attributes:
label: What is the expected behaviour?
description: Simply detail what the expected behaviour is.
placeholder: I expect Vencord/Discord to open the ... page instead of ..., it prevents me from doing ...
validations:
required: true
- type: textarea
id: steps-to-take
attributes:
label: How do you recreate this bug or crash?
description: Give us a list of steps in order to recreate the bug or crash.
placeholder: |
1. Do ...
2. Then ...
3. Do this ..., ... and then ...
4. Observe "the bug" or "the crash"
validations:
required: true
- type: textarea
id: crash-log
attributes:
label: Errors
description: Open the Developer Console with Ctrl/Cmd + Shift + i. Then look for any red errors (Ignore network errors like Failed to load resource) and paste them between the "```".
value: |
```
Replace this text with your crash-log.
```
validations:
required: false
- type: checkboxes
id: agreement-check
attributes:
label: Request Agreement
description: We only accept reports for bugs that happen on Discord Stable. Canary and PTB are Development branches and may be unstable
options:
- label: I am using Discord Stable or tried on Stable and this bug happens there as well
required: true
- label: I have read the requirements for opening an issue above
required: true

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Vencord Support Server
url: https://discord.gg/D9uwnFnqmd
about: If you need help regarding Vencord, please join our support server!
- name: Vencord Installer
url: https://github.com/Vencord/Installer
about: You can find the Vencord Installer here

@ -1,8 +1,15 @@
name: Build latest
name: Build DevBuild
on:
push:
branches:
- main
paths:
- .github/workflows/build.yml
- src/**
- browser/**
- scripts/build/**
- package.json
- pnpm-lock.yaml
env:
FORCE_COLOR: true
@ -15,42 +22,59 @@ jobs:
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
- name: Use Node.js 18
uses: actions/setup-node@v2
- name: Use Node.js 19
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 19
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build web
run: pnpm buildWeb
run: pnpm buildWeb --standalone
- name: Build
run: pnpm build --standalone
- name: Get some values needed for the release
id: vars
shell: bash
- name: Generate plugin list
run: pnpm generatePluginJson dist/plugins.json dist/plugin-readmes.json
- name: Clean up obsolete files
run: |
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
rm -rf dist/*-unpacked dist/monaco Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
- uses: dev-drprasad/delete-tag-and-release@085c6969f18bad0de1b9f3fe6692a3cd01f64fe5 # v0.2.0
with:
delete_release: true
tag_name: devbuild
- name: Get some values needed for the release
id: release_values
run: |
echo "release_tag=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: Upload DevBuild as release
if: github.repository == 'Vendicated/Vencord'
run: |
gh release upload devbuild --clobber dist/*
gh release edit devbuild --title "DevBuild $RELEASE_TAG"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ env.release_tag }}
- name: Create the release
uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5 # v1
- name: Upload DevBuild to builds repo
if: github.repository == 'Vendicated/Vencord'
run: |
git config --global user.name "$USERNAME"
git config --global user.email actions@github.com
git clone https://$USERNAME:$API_TOKEN@github.com/$GH_REPO.git upload
cd upload
GLOBIGNORE=.git:.gitignore:README.md:LICENSE
rm -rf *
cp -r ../dist/* .
git add -A
git commit -m "Builds for https://github.com/$GITHUB_REPOSITORY/commit/$GITHUB_SHA"
git push --force https://$USERNAME:$API_TOKEN@github.com/$GH_REPO.git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: devbuild
name: Dev Build ${{ steps.vars.outputs.sha_short }}
draft: false
prerelease: false
files: |
dist/*
API_TOKEN: ${{ secrets.BUILDS_TOKEN }}
GH_REPO: Vencord/builds
USERNAME: GitHub-Actions

22
.github/workflows/codeberg-mirror.yml vendored Normal file

@ -0,0 +1,22 @@
name: Sync to Codeberg
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
on:
push:
workflow_dispatch:
schedule:
- cron: "0 */6 * * *"
jobs:
codeberg:
if: github.repository == 'Vendicated/Vencord'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: pixta-dev/repository-mirroring-action@674e65a7d483ca28dafaacba0d07351bdcc8bd75 # v1.1.1
with:
target_repo_url: "git@codeberg.org:Ven/cord.git"
ssh_private_key: ${{ secrets.CODEBERG_SSH_PRIVATE_KEY }}

45
.github/workflows/publish.yml vendored Normal file

@ -0,0 +1,45 @@
name: Release Browser Extension
on:
push:
tags:
- v*
jobs:
Publish:
if: github.repository == 'Vendicated/Vencord'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: check that tag matches package.json version
run: |
pkg_version="v$(jq -r .version < package.json)"
if [[ "${{ github.ref_name }}" != "$pkg_version" ]]; then
echo "Tag ${{ github.ref_name }} does not match package.json version $pkg_version" >&2
exit 1
fi
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
- name: Use Node.js 19
uses: actions/setup-node@v3
with:
node-version: 19
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build web
run: pnpm buildWeb --standalone
- name: Publish extension
run: |
cd dist/chromium-unpacked
pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish
env:
EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}

@ -0,0 +1,58 @@
name: Test Patches
on:
workflow_dispatch:
schedule:
# Every day at midnight
- cron: 0 0 * * *
jobs:
TestPlugins:
if: github.repository == 'Vendicated/Vencord'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
- name: Use Node.js 19
uses: actions/setup-node@v3
with:
node-version: 19
cache: "pnpm"
- name: Install dependencies
run: |
pnpm install --frozen-lockfile
pnpm add puppeteer
sudo apt-get install -y chromium-browser
- name: Build web
run: pnpm buildWeb --standalone
- name: Create Report
timeout-minutes: 10
run: |
export PATH="$PWD/node_modules/.bin:$PATH"
export CHROMIUM_BIN=$(which chromium-browser)
esbuild scripts/generateReport.ts > dist/report.mjs
node dist/report.mjs >> $GITHUB_STEP_SUMMARY
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
- name: Create Report (Canary)
timeout-minutes: 10
if: success() || failure() # even run if previous one failed
run: |
export PATH="$PWD/node_modules/.bin:$PATH"
export CHROMIUM_BIN=$(which chromium-browser)
export USE_CANARY=true
esbuild scripts/generateReport.ts > dist/report.mjs
node dist/report.mjs >> $GITHUB_STEP_SUMMARY
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}

@ -15,7 +15,7 @@ jobs:
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
- name: Use Node.js 18
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
@ -23,5 +23,11 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint & Test if it compiles
- name: Lint & Test if desktop version compiles
run: pnpm test
- name: Test if web version compiles
run: pnpm buildWeb
- name: Test if plugin structure is valid
run: pnpm generatePluginJson

4
.gitignore vendored

@ -5,6 +5,7 @@ node_modules
vencord_installer
.idea
.DS_Store
yarn.lock
package-lock.json
@ -18,3 +19,6 @@ lerna-debug.log*
*.tsbuildinfo
src/userplugins
ExtensionCache/
settings/

1
.npmrc Normal file

@ -0,0 +1 @@
strict-peer-dependencies=false

6
.stylelintrc.json Normal file

@ -0,0 +1,6 @@
{
"extends": "stylelint-config-standard",
"rules": {
"indentation": 4
}
}

@ -1,3 +1,11 @@
{
"recommendations": [ "EditorConfig.EditorConfig" ]
"recommendations": [
"dbaeumer.vscode-eslint",
"eamodio.gitlens",
"EditorConfig.EditorConfig",
"ExodiusStudios.comment-anchors",
"formulahendry.auto-rename-tag",
"GregorBiswanger.json2ts",
"stylelint.vscode-stylelint"
]
}

37
.vscode/launch.json vendored Normal file

@ -0,0 +1,37 @@
{
// this allows you to debug Vencord from VSCode.
// How to use:
// You need to run Discord via the command line to pass some flags to it.
// If you want to debug the main (node.js) process (preload.ts, ipcMain/*, patcher.ts),
// add the --inspect flag
// To debug the renderer (99% of Vencord), add the --remote-debugging-port=9223 flag
//
// Now launch the desired configuration in VSCode and start Discord with the flags.
// For example, to debug both process, run Electron: All then launch Discord with
// discord --remote-debugging-port=9223 --inspect
"version": "0.2.0",
"configurations": [
{
"name": "Electron: Main",
"type": "node",
"request": "attach",
"port": 9229,
"timeout": 30000
},
{
"name": "Electron: Renderer",
"type": "chrome",
"request": "attach",
"port": 9223,
"timeout": 30000,
"webRoot": "${workspaceFolder}/src"
}
],
"compounds": [
{
"name": "Electron: All",
"configurations": ["Electron: Main", "Electron: Renderer"]
}
]
}

@ -12,5 +12,12 @@
"javascript.format.semicolons": "insert",
"typescript.format.semicolons": "insert",
"typescript.preferences.quoteStyle": "double",
"javascript.preferences.quoteStyle": "double"
"javascript.preferences.quoteStyle": "double",
"gitlens.remotes": [
{
"domain": "codeberg.org",
"type": "Gitea"
}
]
}

25
.vscode/tasks.json vendored Normal file

@ -0,0 +1,25 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "shell",
"command": "pnpm build",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Watch",
"type": "shell",
"command": "pnpm watch",
"problemMatcher": [],
"group": {
"kind": "build"
}
}
]
}

20
CODE_OF_CONDUCT.md Normal file

@ -0,0 +1,20 @@
# Code of Conduct
Our community is welcoming to everyone, regardless of their characteristics.
As such, we expect you to treat everyone with respect and contribute to an open and welcoming community.
DO
- have empathy and be nice to others
- be respectful of differing opinions, even if you disagree
- give and accept constructive criticism
DON'T
- use offensive or derogatory language
- troll or spam
- personally attack or harass others
Repetitive violations of these guidelines might get your access to the repository restricted.
If you feel like a user is violating these guidelines or feel treated unfairly, please refrain from publicly challenging them and instead contact a Moderator on our Discord server or send an email to vendicated+conduct@riseup.net!

@ -65,7 +65,7 @@ Also pay attention to the following:
- Match any but a guaranteed terminating character: `[^;]+`, for example to match the entire assigned value in `var a=b||c||func();`,
`var .{1,2}=([^;]+);`
- If you don't care about that part, just match a bunch of chars: `.{0,50}`, for example to extract the variable "b" in `createElement("div",{a:"foo",c:"bar"},b)`, `createElement\("div".{0,30},(.{1,2})\),`. Note the `.{0,30}`, this is essentially the same as `.+`, but safer as you can't end up accidently eating thousands of characters
- Additionally, as you might have noticed, all of the appove approaches use regex groups (`(...)`) to capture the variable name. You can then use those groups in your replacement to access those variables dynamically
- Additionally, as you might have noticed, all of the above approaches use regex groups (`(...)`) to capture the variable name. You can then use those groups in your replacement to access those variables dynamically
#### "replace"

@ -1,49 +1,65 @@
# Vencord
A Discord client mod that does things differently
[![Codeberg Mirror](https://img.shields.io/static/v1?style=for-the-badge&label=Codeberg%20Mirror&message=codeberg.org/Ven/cord&color=2185D0&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAKbUlEQVR4nNVae3AV5RX/nW/3Pva+b24e5HHzIICQKGoiYiW8NFBFgohaa6ctglpbFSujSGurzUinohWsOij/gGX6R2fqOK0d1FYTEZXaTrWCBbEikJCEyCvkeXNvkrunf+zdkJDkPnex/c3cmd29+53v/M6e73znnF2Cydj4Tntldzi6qrN/qKqzf2jy6b7BnL4B1dI7oMp9AyoRAIdVsNMqhlxWMZjtspzyK/Jhr036OMsm//bh2vzPzNSPzBD6xFutd7R0Dq758ky4orkjYuc05RCAkixbeEq2/UCJ1/LczxcX/c5IPfU5DMHmxpbCpu7o1k/b+xc1n43YjJI7EqV+W2RmvuPt0oDjB2vn5bQbITNjAzzdeKK8qTO0bU9T77zucNQUjzofHrvENWWu3aUBZfW6+ZOOZiIrbYXrmUXo9daX3v6i667O/iGRiRLpwqtIvKDc+0efJ3hb/UIaSkdGWgZ4sqGt9r2m3lc/P9HvSWe80ZiRp3TPL/UsX1+bvyvVsSkb4NE3WjbuPNj5SM8Fcvdk4bAKrqvwv7DxhuCPUxmXNIn6XSy3nWr6R8OhrqrU1btwqJ3m/bgwu/SqZJdEUgbYsuuka09b9/4Pm3tLMlPvwuAbpe6m+RcplfdcURBKdG9CA2zZddLV2Nx1+JO2vlxj1LswqCpynlxc6SxLZIS40bueWfy9vXvv/xt5APhXa1/u7v+EPqvfxXK8++IaoO2Vpn9+cLS33FjVLhw+bOotOX7q6N/i3TOhAX7y+rHN/+sBLxm8fah71k93tjw/0f/jGuDJxtZrdh7setA8tS4sdn7eef+v3mmfP95/Ywxw6x9Yev9I35/6Iubv83WVfl5a6Uu3VkoavZEo7TnS/Vo98xi+Yy6UKC3bDp7sd5ut1OWFDjyzNMib6oq5Oug0ezp8dqLfG3r92Nbzr48ywNONJ8obDnV/z2xlAk4ZW1aUqhaJIAvCb5YVqwFn3GBtCBoO9dz5TOPxUbnMKAM0dYa2d5lc2AgCNi8r5klui3aBgWynjE11QZbI3FV3NjQkjnYNbB+lj36wubGlcE9T71xTNQDw0Px8nlvmHl73GmfCrKCL19Tkmh4P9jT1LHz2vVP5+vmwAZq71a1m1/PXTPXwD68eS5KIEVUZd1yZwwumeEw1Qld/lJrPhF7Sz4cNsO+rUK2ZExd6rfj10iCPZ2GJCCoAZuCJxQUc9FvNVAX72kPX6ccC0Hp4zR0Ru1kT2mTCSzeXqn5l/EAniMAqoDLDYZWwqa5EVSzmhaKmsxHbLxvbbgdiBmjpHFxj2mwANlxXxBdPUib8nwgQgqAyEFUZxT4L1i/MN3UpHDsTWQvEDHDoTLjCrIluuyzAt8zMSkhGFhp5hrYUFk3z8IqZftOMcKRj4GIAEM80tFccM8n9Z+Qq+MXigqRIWCQCMzQvYIbKwH1X53FFnjkr88iZsLKpoXWa6BiIrjbDzF67hK23lKp2Obm1LAstPEZVjTwDkAio/2ZQ9dolw/VjAB0DfKfoCg9WGy2cADy1NMhBX2rR3CIRGICq8rAhAg4Jj9UWsDBhg+4MR6vF2VC0zGjB99fk8eJp3pQdyyrRMHF9KURVxswCB6+alWO4o3b2RyeLU32D2UYKnVPm5gfm5qWlrF0Wo4hzbCmoDNw0089XlboNNcLpvsFc0RtRDXuNle+x4Lkbi9PO6WWJIBFGEY+qjGjswtq5eVzosRilLnoiUavoH1INiTCyIDy/vETNcmRW1dl0L4gRVxmx3YFhlwnrry1QrZIxASE0yJIIDaiGSHt8UQFXF2Ve1zusYgzxkXGhyGvFvePUE+mgfyAqhGqAqKWVPv5udbYhSjmtkpYWq6OJqzFjqCpjTpmbl1Rk3klSGRBWmTISNC3Hjo1LgoYFJ0GA1aIVR+cTVxlQoS2Pb18a4PLszMKXzSJYuCySmq4Al03CiytKVYfBhYvLKk1IXE+XLRLhwZp81WlNf26HTFHhd0jhdAYTgKduCPLkgPHfQjitYkLiAIEZBDBlu2R6aF7euCV2Mgg45bDw2qWOdAavnp3D109PPdlJBvpTnYg4kVY3MDMuylVw62WJi63x4LHLZ0TAIR9OdWBVodPUclUQwWmT4hLXfgCIUDfDi6oiR8rzBJzyl8LnkD9KZVCOU8aLN5eoshnJ+Qh4bFJC4gztmEjgrtk5anaKnWWfXfpIuBTLjmSpSILw/E0laq7LuGxsIngVCYmIa96hLRG3TaZ1C/KTfjAEQLFIO8TPFk7aH/RZI8kMWrdgEs8udqXLKSUoMkEW4ETEQTRsoHyPlVZfmVw+Uuy3hR9bVHBQAMD0XPu/Ew24dqqH777K/La1DiKCxyYlRRzQymgG4+oyDxZOTdxZnp5r3wvEWmJ5btuL8W4uzbJh87LitLebdOFVpKSJx4IlwIzbL81CcYLO8iSX/IImGQCYae6Wg/2tXQNjNnW7LPDKyilqZd7ETU2zEBlifNTSS4i9PNFIx44x4jh2nZlBsUr0dN8QP/6XVhEaHJvnlfhtkXd/NF0BUextKRFXFznfGk+JDdcX8tdBHtDa6YpFsB4I9ac88omf8wbEgqa2XAIOme6bM35foqrQ+QZIKwGG80ifVbrXZZNGDfhOVYBvviS9JMMoaP3AEcQpPnHdOxiMGXkKbrx4dGfZY5c4T8H9+vmwAeqXFLXOKXW9r59fWuDA44sKv1byAOBzyCkTH+kdS2f4MLPgXJI0p9T17vrFxcf181GVxEUB+0qfIqt+RcKWFSWGNR4ygd4RTpW4HiCJgFWzstmnSPA7ZLU827pypPwxDB/687GXl1X6Vs6bbGz/LRN80hZCT+yLFZ0cgHED4egACeiXm89GsP9EePuzy4rvGil7jAGYmQDsBjDHUBYZ4GhHBMfORigd4rpnyIS9u6d4rqgnGrUtjCmmSYuOqwB0GcwjbWh9xviurpNnxnDA1IspMPe6bOL755MHJvhKjIgOA7jbJD4pw22Thj+kSIW47h2KRaydVezeP57sCdspRPQqgGeNJJIuBAE+ReJUiOv32mXaXjPZs21C2QnmXgdghyEsMoRfkVMiDgCywF/by9z3xJMb1wCxeHAPgDczZpAh/Iq+HSYmDjCsstgThmf5t4ii8eQm7CgS0SCA5QBezoRApnBaBSyCEhIHCLJEb4ZUd+2SqZSwzE+qpUpEQ9CC4qb01M8cRIQsh8zxiKsMtsn08nvlnrpkyAPj5AGJwMw3AtgGwJ/q2ExxvHsQB74KxfKBMblAyGmTHq4pc4/5GjQeUm6qE9FrAK4E8H6ie41GlkN/jTk6F5Ak2ueUpNmpkgfSMAAAENERAAsB3AHgZDoy0oFdFnBYpXPEBfU4beLRD6Z4qmumug+kIzPjaoeZfQDWAHgAQFam8hLh4MkwWjsHemyS2OF08IYrCjynzZ4zKTCzi5nXMvOnzBw16bevIxR95JOj7DNKb1PqXWa+HMDtAGoBXII0lxq0N2OfAmgA8Hsi2muMhudgesHPzNkA5gKoADADwFRoS8UHQO+x9wLoBNAB4AsAnwM4AOADIjLVxf8L9kdXUOE0IskAAAAASUVORK5CYII=)](https://codeberg.org/Ven/cord)
The cutest Discord client mod
![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334)
## Features
- Works on Discord's latest update that breaks all other mods
- Browser Support (experimental): Run Vencord in your Browser instead of the desktop app
- Custom Css and Themes: Manually edit `%appdata%/Vencord/settings/quickCss.css` / `~/.config/Vencord/settings/quickCss.css` with your favourite editor and the client will automatically apply your changes. To import BetterDiscord themes, just add `@import url(theUrl)` on the top of this file. (Make sure the url is a github raw URL or similar and only contains plain text, and NOT a nice looking website)
- Many Useful™ plugins - [List](https://github.com/Vendicated/Vencord/tree/main/src/plugins)
- Experiments
- Proper context isolation -> Works in newer Electron versions (Confirmed working on versions 13-22)
- Inline patches: Patch Discord's code with regex replacements! See [the experiments plugin](src/plugins/experiments.ts) for an example. While being more complex, this is more powerful than monkey patching since you can patch only small parts of functions instead of fully replacing them, access non exported/local variables and even replace constants (like in the aforementioned experiments patch!)
- Super easy to install (Download Installer, open, click install button, done)
- 100+ plugins built in: [See a list](https://vencord.dev/plugins)
- Some highlights: SpotifyControls, MessageLogger, Experiments, GameActivityToggle, Translate, NoTrack, QuickReply, Free Emotes/Stickers, PermissionsViewer, CustomCommands, ShowHiddenChannels, PronounDB
- Fairly lightweight despite the many inbuilt plugins
- Excellent Browser Support: Run Vencord in your Browser via extension or UserScript
- Works on any Discord branch: Stable, Canary or PTB all work (though for the best experience I recommend stable!)
- Custom CSS and Themes: Inbuilt css editor with support to import any css files (including BetterDiscord themes)
- Privacy friendly, blocks Discord analytics & crash reporting out of the box and has no telemetry
- Maintained very actively, broken plugins are usually fixed within 12 hours
- Settings sync: Keep your plugins and their settings synchronised between devices / apps (optional)
## Installing / Uninstalling
Read [Megu's Installation Guide!](docs/1_INSTALLING.md)
Visit https://vencord.dev/download
## Installing on Browser
## Join our Support/Community Server
Run the same commands as in the regular install method. Now run
https://discord.gg/D9uwnFnqmd
```sh
pnpm buildWeb
```
## Sponsors
You will find the built extension at dist/extension.zip. Now just install this extension in your Browser
| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** |
|:--:|
| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) |
| *generated using [github-sponsor-graph](https://github.com/Vendicated/github-sponsor-graph)* |
## Installing Plugins
Vencord comes with a bunch of plugins out of the box!
However, if you want to install your own ones, create a `userplugins` folder in the `src` directory and create or clone your plugins in there.
Don't forget to rebuild!
## Star History
Want to learn how to create your own plugin, and maybe PR it into Vencord? See the [Contributing](#contributing) section below!
<a href="https://star-history.com/#Vendicated/Vencord&Timeline">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Vendicated/Vencord&type=Timeline&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Vendicated/Vencord&type=Timeline" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Vendicated/Vencord&type=Timeline" />
</picture>
</a>
## Contributing
## Disclaimer
See [CONTRIBUTING.md](CONTRIBUTING.md) and [Megu's Plugin Guide!](docs/2_PLUGINS.md)
Discord is trademark of Discord Inc. and solely mentioned for the sake of descriptivity.
Mention of it does not imply any affiliation with or endorsement by Discord Inc.
[contribute]: CONTRIBUTING.md
<details>
<summary>Using Vencord violates Discord's terms of service</summary>
[contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute] [contribute]
Client modifications are against Discords Terms of Service.
## Join
However, Discord is pretty indifferent about them and there are no known cases of users getting banned for using client mods! So you should generally be fine as long as you dont use any plugins that implement abusive behaviour. But no worries, all inbuilt plugins are safe to use!
[join]: https://discord.gg/D9uwnFnqmd
Regardless, if your account is very important to you and it getting disabled would be a disaster for you, you should probably not use any client mods (not exclusive to Vencord), just to be safe
[join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join] [join]
Additionally, make sure not to post screenshots with Vencord in a server where you might get banned for it
</details>

75
browser/GMPolyfill.js Normal file

@ -0,0 +1,75 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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/>.
*/
function parseHeaders(headers) {
if (!headers)
return {};
const result = {};
const headersArr = headers.trim().split("\n");
for (var i = 0; i < headersArr.length; i++) {
var row = headersArr[i];
var index = row.indexOf(":")
, key = row.slice(0, index).trim().toLowerCase()
, value = row.slice(index + 1).trim();
if (result[key] === undefined) {
result[key] = value;
} else if (Array.isArray(result[key])) {
result[key].push(value);
} else {
result[key] = [result[key], value];
}
}
return result;
}
function blobTo(to, blob) {
if (to === "arrayBuffer" && blob.arrayBuffer) return blob.arrayBuffer();
return new Promise((resolve, reject) => {
var fileReader = new FileReader();
fileReader.onload = event => resolve(event.target.result);
if (to === "arrayBuffer") fileReader.readAsArrayBuffer(blob);
else if (to === "text") fileReader.readAsText(blob, "utf-8");
else reject("unknown to");
});
}
function GM_fetch(url, opt) {
return new Promise((resolve, reject) => {
// https://www.tampermonkey.net/documentation.php?ext=dhdg#GM_xmlhttpRequest
const options = opt || {};
options.url = url;
options.data = options.body;
options.responseType = "blob";
options.onload = resp => {
var blob = resp.response;
resp.blob = () => Promise.resolve(blob);
resp.arrayBuffer = () => blobTo("arrayBuffer", blob);
resp.text = () => blobTo("text", blob);
resp.json = async () => JSON.parse(await blobTo("text", blob));
resp.headers = new Headers(parseHeaders(resp.responseHeaders));
resp.ok = resp.status >= 200 && resp.status < 300;
resolve(resp);
};
options.ontimeout = () => reject("fetch timeout");
options.onerror = () => reject("fetch error");
options.onabort = () => reject("fetch abort");
GM_xmlhttpRequest(options);
});
}
export const fetch = GM_fetch;

@ -16,51 +16,90 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import IpcEvents from "../src/utils/IpcEvents";
/// <reference path="../src/modules.d.ts" />
/// <reference path="../src/globals.d.ts" />
import monacoHtmlLocal from "~fileContent/monacoWin.html";
import monacoHtmlCdn from "~fileContent/../src/main/monacoWin.html";
import * as DataStore from "../src/api/DataStore";
import { debounce } from "../src/utils";
import { EXTENSION_BASE_URL } from "../src/utils/web-metadata";
import { getTheme, Theme } from "../src/utils/discord";
import { getThemeInfo } from "../src/main/themes";
// Discord deletes this so need to store in variable
const { localStorage } = window;
// listeners for ipc.on
const listeners = {} as Record<string, Set<Function>>;
const cssListeners = new Set<(css: string) => void>();
const NOOP = () => { };
const NOOP_ASYNC = async () => { };
const handlers = {
[IpcEvents.GET_REPO]: () => "https://github.com/Vendicated/Vencord", // shrug
[IpcEvents.GET_SETTINGS_DIR]: () => "LocalStorage",
const setCssDebounced = debounce((css: string) => VencordNative.quickCss.set(css));
[IpcEvents.GET_QUICK_CSS]: () => DataStore.get("VencordQuickCss").then(s => s ?? ""),
[IpcEvents.SET_QUICK_CSS]: (css: string) => {
DataStore.set("VencordQuickCss", css);
listeners[IpcEvents.QUICK_CSS_UPDATE]?.forEach(l => l(null, css));
},
[IpcEvents.GET_SETTINGS]: () => localStorage.getItem("VencordSettings") || "{}",
[IpcEvents.SET_SETTINGS]: (s: string) => localStorage.setItem("VencordSettings", s),
[IpcEvents.GET_UPDATES]: () => ({ ok: true, value: [] }),
[IpcEvents.OPEN_EXTERNAL]: (url: string) => open(url, "_blank"),
};
function onEvent(event: string, ...args: any[]) {
const handler = handlers[event];
if (!handler) throw new Error(`Event ${event} not implemented.`);
return handler(...args);
}
const themeStore = DataStore.createStore("VencordThemes", "VencordThemeData");
// probably should make this less cursed at some point
window.VencordNative = {
getVersions: () => ({}),
ipc: {
send: (event: string, ...args: any[]) => void onEvent(event, ...args),
sendSync: onEvent,
on(event: string, listener: () => {}) {
(listeners[event] ??= new Set()).add(listener);
},
off(event: string, listener: () => {}) {
return listeners[event]?.delete(listener);
},
invoke: (event: string, ...args: any[]) => Promise.resolve(onEvent(event, ...args))
themes: {
uploadTheme: (fileName: string, fileData: string) => DataStore.set(fileName, fileData, themeStore),
deleteTheme: (fileName: string) => DataStore.del(fileName, themeStore),
getThemesDir: async () => "",
getThemesList: () => DataStore.entries(themeStore).then(entries =>
entries.map(([name, css]) => getThemeInfo(css, name.toString()))
),
getThemeData: (fileName: string) => DataStore.get(fileName, themeStore),
getSystemValues: async () => ({}),
},
native: {
getVersions: () => ({}),
openExternal: async (url) => void open(url, "_blank")
},
updater: {
getRepo: async () => ({ ok: true, value: "https://github.com/Vendicated/Vencord" }),
getUpdates: async () => ({ ok: true, value: [] }),
update: async () => ({ ok: true, value: false }),
rebuild: async () => ({ ok: true, value: true }),
},
quickCss: {
get: () => DataStore.get("VencordQuickCss").then(s => s ?? ""),
set: async (css: string) => {
await DataStore.set("VencordQuickCss", css);
cssListeners.forEach(l => l(css));
},
addChangeListener(cb) {
cssListeners.add(cb);
},
addThemeChangeListener: NOOP,
openFile: NOOP_ASYNC,
async openEditor() {
const features = `popup,width=${Math.min(window.innerWidth, 1000)},height=${Math.min(window.innerHeight, 1000)}`;
const win = open("about:blank", "VencordQuickCss", features);
if (!win) {
alert("Failed to open QuickCSS popup. Make sure to allow popups!");
return;
}
win.baseUrl = EXTENSION_BASE_URL;
win.setCss = setCssDebounced;
win.getCurrentCss = () => VencordNative.quickCss.get();
win.getTheme = () =>
getTheme() === Theme.Light
? "vs-light"
: "vs-dark";
win.document.write(IS_EXTENSION ? monacoHtmlLocal : monacoHtmlCdn);
},
},
settings: {
get: () => localStorage.getItem("VencordSettings") || "{}",
set: async (s: string) => localStorage.setItem("VencordSettings", s),
getSettingsDir: async () => "LocalStorage"
},
pluginHelpers: {} as any,
};

@ -1,24 +1,32 @@
if (typeof browser === "undefined") {
var browser = chrome;
/**
* @template T
* @param {T[]} arr
* @param {(v: T) => boolean} predicate
*/
function removeFirst(arr, predicate) {
const idx = arr.findIndex(predicate);
if (idx !== -1) arr.splice(idx, 1);
}
browser.webRequest.onHeadersReceived.addListener(({ responseHeaders, url }) => {
const cspIdx = responseHeaders.findIndex(h => h.name === "content-security-policy");
if (cspIdx !== -1)
responseHeaders.splice(cspIdx, 1);
chrome.webRequest.onHeadersReceived.addListener(
({ responseHeaders, type, url }) => {
if (!responseHeaders) return;
if (url.endsWith(".css")) {
const contentType = responseHeaders.find(h => h.name === "content-type");
if (contentType)
contentType.value = "text/css";
else
if (type === "main_frame") {
// In main frame requests, the CSP needs to be removed to enable fetching of custom css
// as desired by the user
removeFirst(responseHeaders, h => h.name.toLowerCase() === "content-security-policy");
} else if (type === "stylesheet" && url.startsWith("https://raw.githubusercontent.com/")) {
// Most users will load css from GitHub, but GitHub doesn't set the correct content type,
// so we fix it here
removeFirst(responseHeaders, h => h.name.toLowerCase() === "content-type");
responseHeaders.push({
name: "content-type",
value: "text/json"
name: "Content-Type",
value: "text/css"
});
}
return {
responseHeaders
};
}, { urls: ["*://*.discord.com/*"] }, ["blocking", "responseHeaders"]);
}
return { responseHeaders };
},
{ urls: ["https://raw.githubusercontent.com/*", "*://*.discord.com/*"], types: ["main_frame", "stylesheet"] },
["blocking", "responseHeaders"]
);

@ -2,7 +2,23 @@ if (typeof browser === "undefined") {
var browser = chrome;
}
var script = document.createElement("script");
const script = document.createElement("script");
script.src = browser.runtime.getURL("dist/Vencord.js");
// documentElement because we load before body/head are ready
document.documentElement.appendChild(script);
script.id = "vencord-script";
Object.assign(script.dataset, {
extensionBaseUrl: browser.runtime.getURL(""),
version: browser.runtime.getManifest().version
});
const style = document.createElement("link");
style.type = "text/css";
style.rel = "stylesheet";
style.href = browser.runtime.getURL("dist/Vencord.css");
document.documentElement.append(script);
document.addEventListener(
"DOMContentLoaded",
() => document.documentElement.append(style),
{ once: true }
);

BIN
browser/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -1,32 +1,45 @@
{
"manifest_version": 2,
"manifest_version": 3,
"minimum_chrome_version": "91",
"name": "Vencord Web",
"description": "Yeee",
"version": "1.0.0",
"description": "The cutest Discord mod now in your browser",
"author": "Vendicated",
"homepage_url": "https://github.com/Vendicated/Vencord",
"background": {
"scripts": [
"background.js"
]
"icons": {
"128": "icon.png"
},
"host_permissions": [
"*://*.discord.com/*",
"https://raw.githubusercontent.com/*"
],
"permissions": ["declarativeNetRequest"],
"content_scripts": [
{
"run_at": "document_start",
"matches": [
"*://*.discord.com/*"
],
"js": [
"content.js"
]
"matches": ["*://*.discord.com/*"],
"js": ["content.js"],
"all_frames": true
}
],
"permissions": [
"*://*.discord.com/*",
"webRequest",
"webRequestBlocking"
],
"web_accessible_resources": [
"dist/Vencord.js"
]
{
"resources": ["dist/*", "third-party/*"],
"matches": ["*://*.discord.com/*"]
}
],
"declarative_net_request": {
"rule_resources": [
{
"id": "modifyResponseHeaders",
"enabled": true,
"path": "modifyResponseHeaders.json"
}
]
}
}

41
browser/manifestv2.json Normal file

@ -0,0 +1,41 @@
{
"manifest_version": 2,
"minimum_chrome_version": "91",
"name": "Vencord Web",
"description": "The cutest Discord mod now in your browser",
"author": "Vendicated",
"homepage_url": "https://github.com/Vendicated/Vencord",
"icons": {
"128": "icon.png"
},
"permissions": [
"webRequest",
"webRequestBlocking",
"*://*.discord.com/*",
"https://raw.githubusercontent.com/*"
],
"content_scripts": [
{
"run_at": "document_start",
"matches": ["*://*.discord.com/*"],
"js": ["content.js"],
"all_frames": true
}
],
"background": {
"scripts": ["background.js"]
},
"web_accessible_resources": ["dist/Vencord.js", "dist/Vencord.css"],
"browser_specific_settings": {
"gecko": {
"id": "vencord-firefox@vendicated.dev",
"strict_min_version": "91.0"
}
}
}

@ -0,0 +1,38 @@
[
{
"id": 1,
"action": {
"type": "modifyHeaders",
"responseHeaders": [
{
"header": "content-security-policy",
"operation": "remove"
},
{
"header": "content-security-policy-report-only",
"operation": "remove"
}
]
},
"condition": {
"resourceTypes": ["main_frame", "sub_frame"]
}
},
{
"id": 2,
"action": {
"type": "modifyHeaders",
"responseHeaders": [
{
"header": "content-type",
"operation": "set",
"value": "text/css"
}
]
},
"condition": {
"resourceTypes": ["stylesheet"],
"urlFilter": "https://raw.githubusercontent.com/*"
}
}
]

43
browser/monaco.ts Normal file

@ -0,0 +1,43 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./patch-worker";
import * as monaco from "monaco-editor/esm/vs/editor/editor.main.js";
declare global {
const baseUrl: string;
const getCurrentCss: () => Promise<string>;
const setCss: (css: string) => void;
const getTheme: () => string;
}
const BASE = "/dist/monaco/vs";
self.MonacoEnvironment = {
getWorkerUrl(_moduleId: unknown, label: string) {
const path = label === "css" ? "/language/css/css.worker.js" : "/editor/editor.worker.js";
return new URL(BASE + path, baseUrl).toString();
}
};
getCurrentCss().then(css => {
const editor = monaco.editor.create(
document.getElementById("container")!,
{
value: css,
language: "css",
theme: getTheme(),
}
);
editor.onDidChangeModelContent(() =>
setCss(editor.getValue())
);
window.addEventListener("resize", () => {
// make monaco re-layout
editor.layout();
});
});

37
browser/monacoWin.html Normal file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Vencord QuickCSS Editor</title>
<style>
html,
body,
#container {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
const script = document.createElement("script");
script.src = new URL("/dist/monaco/index.js", baseUrl);
const style = document.createElement("link");
style.type = "text/css";
style.rel = "stylesheet";
style.href = new URL("/dist/monaco/index.css", baseUrl);
document.body.append(style, script);
</script>
</body>
</html>

135
browser/patch-worker.js Normal file

@ -0,0 +1,135 @@
/*
Copyright 2013 Rob Wu <gwnRob@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Target: Chrome 20+
// W3-compliant Worker proxy.
// This module replaces the global Worker object.
// When invoked, the default Worker object is called.
// If this call fails with SECURITY_ERR, the script is fetched
// using async XHR, and transparently proxies all calls and
// setters/getters to the new Worker object.
// Note: This script does not magically circumvent the Same origin policy.
(function () {
'use strict';
var Worker_ = window.Worker;
var URL = window.URL || window.webkitURL;
// Create dummy worker for the following purposes:
// 1. Don't override the global Worker object if the fallback isn't
// going to work (future API changes?)
// 2. Use it to trigger early validation of postMessage calls
// Note: Blob constructor is supported since Chrome 20, but since
// some of the used Chrome APIs are only supported as of Chrome 20,
// I don't bother adding a BlobBuilder fallback.
var dummyWorker = new Worker_(
URL.createObjectURL(new Blob([], { type: 'text/javascript' })));
window.Worker = function Worker(scriptURL) {
if (arguments.length === 0) {
throw new TypeError('Not enough arguments');
}
try {
return new Worker_(scriptURL);
} catch (e) {
if (e.code === 18/*DOMException.SECURITY_ERR*/) {
return new WorkerXHR(scriptURL);
} else {
throw e;
}
}
};
// Bind events and replay queued messages
function bindWorker(worker, workerURL) {
if (worker._terminated) {
return;
}
worker.Worker = new Worker_(workerURL);
worker.Worker.onerror = worker._onerror;
worker.Worker.onmessage = worker._onmessage;
var o;
while ((o = worker._replayQueue.shift())) {
worker.Worker[o.method].apply(worker.Worker, o.arguments);
}
while ((o = worker._messageQueue.shift())) {
worker.Worker.postMessage.apply(worker.Worker, o);
}
}
function WorkerXHR(scriptURL) {
var worker = this;
var x = new XMLHttpRequest();
x.responseType = 'blob';
x.onload = function () {
// http://stackoverflow.com/a/10372280/938089
var workerURL = URL.createObjectURL(x.response);
bindWorker(worker, workerURL);
};
x.open('GET', scriptURL);
x.send();
worker._replayQueue = [];
worker._messageQueue = [];
}
WorkerXHR.prototype = {
constructor: Worker_,
terminate: function () {
if (!this._terminated) {
this._terminated = true;
if (this.Worker)
this.Worker.terminate();
}
},
postMessage: function (message, transfer) {
if (!(this instanceof WorkerXHR))
throw new TypeError('Illegal invocation');
if (this.Worker) {
this.Worker.postMessage.apply(this.Worker, arguments);
} else {
// Trigger validation:
dummyWorker.postMessage(message);
// Alright, push the valid message to the queue.
this._messageQueue.push(arguments);
}
}
};
// Implement the EventTarget interface
[
'addEventListener',
'removeEventListener',
'dispatchEvent'
].forEach(function (method) {
WorkerXHR.prototype[method] = function () {
if (!(this instanceof WorkerXHR)) {
throw new TypeError('Illegal invocation');
}
if (this.Worker) {
this.Worker[method].apply(this.Worker, arguments);
} else {
this._replayQueue.push({ method: method, arguments: arguments });
}
};
});
Object.defineProperties(WorkerXHR.prototype, {
onmessage: {
get: function () { return this._onmessage || null; },
set: function (func) {
this._onmessage = typeof func === 'function' ? func : null;
}
},
onerror: {
get: function () { return this._onerror || null; },
set: function (func) {
this._onerror = typeof func === 'function' ? func : null;
}
}
});
})();

@ -7,7 +7,7 @@
// @supportURL https://github.com/Vendicated/Vencord
// @license GPL-3.0
// @match *://*.discord.com/*
// @grant none
// @grant GM_xmlhttpRequest
// @run-at document-start
// @compatible chrome Chrome + Tampermonkey or Violentmonkey
// @compatible firefox Firefox Tampermonkey

@ -1,3 +0,0 @@
// FIXME: Delete this soon, for now it is needed so people can update
import("./scripts/build/build.mjs");

@ -1,3 +1,7 @@
> [!WARNING]
> These instructions are only for advanced users. If you're not a Developer, you should use our [graphical installer](https://github.com/Vendicated/VencordInstaller#usage) instead.
> No support will be provided for installing in this fashion. If you cannot figure it out, you should just stick to a regular install.
# Installation Guide
Welcome to Megu's Installation Guide! In this file, you will learn about how to download, install, and uninstall Vencord!
@ -10,12 +14,6 @@ Welcome to Megu's Installation Guide! In this file, you will learn about how to
- [Installing Vencord](#installing-vencord)
- [Updating Vencord](#updating-vencord)
- [Uninstalling Vencord](#uninstalling-vencord)
- [Manually Installing Vencord](#manually-installing-vencord)
- [On Windows](#on-windows)
- [On Linux](#on-linux)
- [On MacOS](#on-macos)
- [Manual Patching](#manual-patching)
- [Manually Uninstalling Vencord](#manually-uninstalling-vencord)
## Dependencies
@ -24,16 +22,16 @@ Welcome to Megu's Installation Guide! In this file, you will learn about how to
## Installing Vencord
> :exclamation: If this doesn't work, see [Manually Installing Vencord](#manually-installing-vencord)
Install `pnpm`:
> :exclamation: this may need to be run as admin depending on your system, and you may need to close and reopen your terminal.
> :exclamation: This next command may need to be run as admin/root depending on your system, and you may need to close and reopen your terminal for pnpm to be in your PATH.
```shell
npm i -g pnpm
```
> :exclamation: **IMPORTANT** Make sure you aren't using an admin/root terminal from here onwards. It **will** mess up your Discord/Vencord instance and you **will** most likely have to reinstall.
Clone Vencord:
```shell
@ -65,7 +63,7 @@ Then fully close Discord from your taskbar or task manager, and restart it. Venc
If you're using Discord already, go into the `Updater` tab in settings.
Sometimes it may be neccessary to manually update if the GUI updater fails.
Sometimes it may be necessary to manually update if the GUI updater fails.
To pull latest changes:
@ -98,103 +96,4 @@ Simply run:
pnpm uninject
```
The above command may ask you to also run:
```shell
pnpm install --frozen-lockfile
pnpm uninject
```
## Manually Installing Vencord
- [Windows](#on-windows)
- [Linux](#on-linux)
- [MacOS](#on-macos)
### On Windows
Press Win+R and enter: `%LocalAppData%` and hit enter. In this page, find the page (Discord, DiscordPTB, DiscordCanary, etc) that you want to patch.
Now follow the instructions at [Manual Patching](#manual-patching)
### On Linux
The Discord folder is usually in one of the following paths:
- /usr/share
- /usr/lib64
- /opt
- /home/$USER/.local/share
If you use flatpak, it will usually be in one of the following paths:
- /var/lib/flatpak/app/com.discordapp.Discord/current/active/files
- /home/$USER/.local/share/flatpak/app/com.discordapp.Discord/current/active/files
You will need to give flatpak access to vencord with one of the following commands:
> :exclamation: If not on stable, replace `com.discordapp.Discord` with your branch name, e.g., `com.discordapp.DiscordCanary`
> :exclamation: Replace `/path/to/vencord/` with the path to your vencord folder (NOT the dist folder)
If Discord flatpak install is in /home/:
```shell
flatpak override --user com.discordapp.Discord --filesystem="/path/to/vencord/"
```
If Discord flatpak install not in /home/:
```shell
sudo flatpak override com.discordapp.Discord --filesystem="/path/to/vencord"
```
Now follow the instructions at [Manual Patching](#manual-patching)
### On MacOS
Open finder and go to your Applications folder. Right-Click on the Discord application you want to patch, and view contents.
Go to the `Contents/Resources` folder.
Now follow the instructions at [Manual Patching](#manual-patching)
### Manual Patching
> :exclamation: If using Flatpak on linux, go to the folder that contains the `app.asar` file, and skip to where we create the `app` folder below.
> :exclamation: On Linux/MacOS, there's a chance there won't be an `app-<number>` folder, but there probably is a `resources` folder, so keep reading :)
Inside there, look for the `app-<number>` folders. If you have multiple, use the highest number. If that doesn't work, do it for the rest of the `app-<number>` folders.
Inside there, go to the `resources` folder. There should be a file called `app.asar`. If there isn't, look at a different `app-<number>` folder instead.
Make a new folder in `resources` called `app`. In here, we will make two files:
`package.json` and `index.js`
In `index.js`:
> :exclamation: Replace the path in the first line with the path to `patcher.js` in your vencord dist folder.
> On Windows, you can get this by shift-rightclicking the patcher.js file and selecting "copy as path"
```js
require("C:/Users/<your user>/path/to/vencord/dist/patcher.js");
require("../app.asar");
```
And in `package.json`:
```json
{ "name": "discord", "main": "index.js" }
```
Finally, fully close & reopen your Discord client and check to see that `Vencord` appears in settings!
### Manually Uninstalling Vencord
> :exclamation: Do not delete `app.asar` - Only delete the `app` folder we created.
Use the instructions above to find the `app` folder, and delete it. Then Close & Reopen Discord.
If you need more help, ask in the support channel in our [Discord Server](https://discord.gg/D9uwnFnqmd).

@ -15,7 +15,7 @@ You don't need to run `pnpm build` every time you make a change. Instead, use `p
3. In `index.ts`, copy-paste the following template code:
```ts
import definePlugin from "../../utils/types";
import definePlugin from "@utils/types";
export default definePlugin({
name: "Epic Plugin",

@ -1,9 +1,8 @@
{
"name": "vencord",
"private": "true",
"version": "1.0.0",
"description": "A Discord client mod that does things differently",
"keywords": [],
"version": "1.6.3",
"description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": {
"url": "https://github.com/Vendicated/Vencord/issues"
@ -12,7 +11,7 @@
"type": "git",
"url": "git+https://github.com/Vendicated/Vencord.git"
},
"license": "GPL-3.0",
"license": "GPL-3.0-or-later",
"author": "Vendicated",
"directories": {
"doc": "docs"
@ -20,32 +19,85 @@
"scripts": {
"build": "node scripts/build/build.mjs",
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs",
"inject": "node scripts/patcher/install.js",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"generatePluginJson": "tsx scripts/generatePluginList.ts",
"inject": "node scripts/runInstaller.mjs",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern src/userplugins",
"lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins",
"lint:fix": "pnpm lint --fix",
"test": "pnpm lint && pnpm build && pnpm testTsc",
"test": "pnpm build && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson",
"testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc",
"testTsc": "tsc --noEmit",
"uninject": "node scripts/patcher/uninstall.js",
"uninject": "node scripts/runInstaller.mjs",
"watch": "node scripts/build/build.mjs --watch"
},
"dependencies": {
"console-menu": "^0.1.0",
"fflate": "^0.7.4"
"@sapphi-red/web-noise-suppressor": "0.3.3",
"@vap/core": "0.0.12",
"@vap/shiki": "0.10.5",
"eslint-plugin-simple-header": "^1.0.2",
"fflate": "^0.7.4",
"gifenc": "github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3",
"monaco-editor": "^0.43.0",
"nanoid": "^4.0.2",
"virtual-merge": "^1.0.1"
},
"devDependencies": {
"@types/node": "^18.7.13",
"@types/react": "^18.0.17",
"@types/chrome": "^0.0.246",
"@types/diff": "^5.0.3",
"@types/lodash": "^4.14.194",
"@types/node": "^18.16.3",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.1",
"@types/yazl": "^2.4.2",
"@typescript-eslint/parser": "^5.39.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"diff": "^5.1.0",
"discord-types": "^1.3.26",
"esbuild": "^0.15.5",
"eslint": "^8.24.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-simple-import-sort": "^8.0.0",
"esbuild": "^0.15.18",
"eslint": "^8.46.0",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-path-alias": "^1.0.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"highlight.js": "10.6.0",
"moment": "^2.29.4",
"puppeteer-core": "^19.11.1",
"standalone-electron-types": "^1.0.0",
"type-fest": "^3.1.0",
"typescript": "^4.8.4"
"stylelint": "^15.6.0",
"stylelint-config-standard": "^33.0.0",
"tsx": "^3.12.7",
"type-fest": "^3.9.0",
"typescript": "^5.0.4",
"zip-local": "^0.3.5"
},
"packageManager": "pnpm@7.13.4"
"packageManager": "pnpm@8.10.2",
"pnpm": {
"patchedDependencies": {
"eslint-plugin-path-alias@1.0.0": "patches/eslint-plugin-path-alias@1.0.0.patch",
"eslint@8.46.0": "patches/eslint@8.46.0.patch"
},
"peerDependencyRules": {
"ignoreMissing": [
"eslint-plugin-import",
"eslint"
]
},
"allowedDeprecatedVersions": {
"source-map-resolve": "*",
"resolve-url": "*",
"source-map-url": "*",
"urix": "*"
}
},
"webExt": {
"artifactsDir": "./dist",
"build": {
"overwriteDest": true
},
"sourceDir": "./dist/firefox-unpacked"
},
"engines": {
"node": ">=18",
"pnpm": ">=8"
}
}

@ -0,0 +1,13 @@
diff --git a/lib/rules/no-relative.js b/lib/rules/no-relative.js
index 71594c83f1f4f733ffcc6047d7f7084348335dbe..d8623d87c89499c442171db3272cba07c9efabbe 100644
--- a/lib/rules/no-relative.js
+++ b/lib/rules/no-relative.js
@@ -41,7 +41,7 @@ module.exports = {
ImportDeclaration(node) {
const importPath = node.source.value;
- if (!/^(\.?\.\/)/.test(importPath)) {
+ if (!/^(\.\.\/)/.test(importPath)) {
return;
}

@ -0,0 +1,49 @@
diff --git a/lib/rules/no-useless-escape.js b/lib/rules/no-useless-escape.js
index 0e0f6f09f2c35f3276173c08f832cde9f2cf56a0..7dc22851715f3574d935f513c1b5e35552985711 100644
--- a/lib/rules/no-useless-escape.js
+++ b/lib/rules/no-useless-escape.js
@@ -65,13 +65,31 @@ module.exports = {
escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
},
- schema: []
+ schema: [{
+ type: "object",
+ properties: {
+ extra: {
+ type: "string",
+ default: ""
+ },
+ extraCharClass: {
+ type: "string",
+ default: ""
+ },
+ },
+ additionalProperties: false
+ }]
},
create(context) {
+ const options = context.options[0] || {};
+ const { extra, extraCharClass } = options;
const sourceCode = context.sourceCode;
const parser = new RegExpParser();
+ const NON_CHARCLASS_ESCAPES = union(REGEX_NON_CHARCLASS_ESCAPES, new Set(extra));
+ const CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set(extraCharClass));
+
/**
* Reports a node
* @param {ASTNode} node The node to report
@@ -200,9 +218,9 @@ module.exports = {
let allowedEscapes;
if (characterClassStack.length) {
- allowedEscapes = unicodeSets ? REGEX_CLASSSET_CHARACTER_ESCAPES : REGEX_GENERAL_ESCAPES;
+ allowedEscapes = unicodeSets ? REGEX_CLASSSET_CHARACTER_ESCAPES : CHARCLASS_ESCAPES;
} else {
- allowedEscapes = REGEX_NON_CHARCLASS_ESCAPES;
+ allowedEscapes = NON_CHARCLASS_ESCAPES;
}
if (allowedEscapes.has(escapedChar)) {
return;

File diff suppressed because it is too large Load Diff

@ -18,23 +18,25 @@
*/
import esbuild from "esbuild";
import { readdir } from "fs/promises";
import { join } from "path";
import { commonOpts, gitHash, globPlugins, isStandalone } from "./common.mjs";
import { BUILD_TIMESTAMP, commonOpts, existsAsync, globPlugins, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs";
const defines = {
IS_STANDALONE: isStandalone
IS_STANDALONE: isStandalone,
IS_DEV: JSON.stringify(watch),
IS_UPDATER_DISABLED: updaterDisabled,
IS_WEB: false,
IS_EXTENSION: false,
VERSION: JSON.stringify(VERSION),
BUILD_TIMESTAMP,
};
if (defines.IS_STANDALONE === "false")
// If this is a local build (not standalone), optimise
// for the specific platform we're on
defines["process.platform"] = JSON.stringify(process.platform);
const header = `
// Vencord ${gitHash}
// Standalone: ${defines.IS_STANDALONE}
// Platform: ${defines["process.platform"] || "Universal"}
`.trim();
/**
* @type {esbuild.BuildOptions}
*/
@ -43,29 +45,76 @@ const nodeCommonOpts = {
format: "cjs",
platform: "node",
target: ["esnext"],
minify: true,
bundle: true,
external: ["electron", ...commonOpts.external],
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external],
define: defines,
banner: {
js: header
};
const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`;
const sourcemap = watch ? "inline" : "external";
/**
* @type {import("esbuild").Plugin}
*/
const globNativesPlugin = {
name: "glob-natives-plugin",
setup: build => {
const filter = /^~pluginNatives$/;
build.onResolve({ filter }, args => {
return {
namespace: "import-natives",
path: args.path
};
});
build.onLoad({ filter, namespace: "import-natives" }, async () => {
const pluginDirs = ["plugins", "userplugins"];
let code = "";
let natives = "\n";
let i = 0;
for (const dir of pluginDirs) {
const dirPath = join("src", dir);
if (!await existsAsync(dirPath)) continue;
const plugins = await readdir(dirPath);
for (const p of plugins) {
if (!await existsAsync(join(dirPath, p, "native.ts"))) continue;
const nameParts = p.split(".");
const namePartsWithoutTarget = nameParts.length === 1 ? nameParts : nameParts.slice(0, -1);
// pluginName.thing.desktop -> PluginName.thing
const cleanPluginName = p[0].toUpperCase() + namePartsWithoutTarget.join(".").slice(1);
const mod = `p${i}`;
code += `import * as ${mod} from "./${dir}/${p}/native";\n`;
natives += `${JSON.stringify(cleanPluginName)}:${mod},\n`;
i++;
}
}
code += `export default {${natives}};`;
return {
contents: code,
resolveDir: "./src"
};
});
}
};
await Promise.all([
// Discord Desktop main & renderer & preload
esbuild.build({
...nodeCommonOpts,
entryPoints: ["src/preload.ts"],
outfile: "dist/preload.js",
footer: { js: "//# sourceURL=VencordPreload\n//# sourceMappingURL=vencord://preload.js.map" },
sourcemap: "external",
}),
esbuild.build({
...nodeCommonOpts,
entryPoints: ["src/patcher.ts"],
entryPoints: ["src/main/index.ts"],
outfile: "dist/patcher.js",
footer: { js: "//# sourceURL=VencordPatcher\n//# sourceMappingURL=vencord://patcher.js.map" },
sourcemap: "external",
footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") },
sourcemap,
define: {
...defines,
IS_DISCORD_DESKTOP: true,
IS_VESKTOP: false
},
plugins: [
...nodeCommonOpts.plugins,
globNativesPlugin
]
}),
esbuild.build({
...commonOpts,
@ -73,16 +122,78 @@ await Promise.all([
outfile: "dist/renderer.js",
format: "iife",
target: ["esnext"],
footer: { js: "//# sourceURL=VencordRenderer\n//# sourceMappingURL=vencord://renderer.js.map" },
footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") },
globalName: "Vencord",
sourcemap: "external",
sourcemap,
plugins: [
globPlugins,
globPlugins("discordDesktop"),
...commonOpts.plugins
],
define: {
IS_WEB: "false",
IS_STANDALONE: isStandalone
...defines,
IS_DISCORD_DESKTOP: true,
IS_VESKTOP: false
}
}),
esbuild.build({
...nodeCommonOpts,
entryPoints: ["src/preload.ts"],
outfile: "dist/preload.js",
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
sourcemap,
define: {
...defines,
IS_DISCORD_DESKTOP: true,
IS_VESKTOP: false
}
}),
// Vencord Desktop main & renderer & preload
esbuild.build({
...nodeCommonOpts,
entryPoints: ["src/main/index.ts"],
outfile: "dist/vencordDesktopMain.js",
footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") },
sourcemap,
define: {
...defines,
IS_DISCORD_DESKTOP: false,
IS_VESKTOP: true
},
plugins: [
...nodeCommonOpts.plugins,
globNativesPlugin
]
}),
esbuild.build({
...commonOpts,
entryPoints: ["src/Vencord.ts"],
outfile: "dist/vencordDesktopRenderer.js",
format: "iife",
target: ["esnext"],
footer: { js: "//# sourceURL=VencordDesktopRenderer\n" + sourceMapFooter("vencordDesktopRenderer") },
globalName: "Vencord",
sourcemap,
plugins: [
globPlugins("vencordDesktop"),
...commonOpts.plugins
],
define: {
...defines,
IS_DISCORD_DESKTOP: false,
IS_VESKTOP: true
}
}),
esbuild.build({
...nodeCommonOpts,
entryPoints: ["src/preload.ts"],
outfile: "dist/vencordDesktopPreload.js",
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("vencordDesktopPreload") },
sourcemap,
define: {
...defines,
IS_DISCORD_DESKTOP: false,
IS_VESKTOP: true
}
}),
]).catch(err => {

175
scripts/build/buildWeb.mjs Executable file → Normal file

@ -17,16 +17,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import esbuild from "esbuild";
import { zip } from "fflate";
import { readFileSync, writeFileSync } from "fs";
import { readFile } from "fs/promises";
import { readFileSync } from "fs";
import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
import { join } from "path";
import Zip from "zip-local";
// wtf is this assert syntax
import PackageJSON from "../../package.json" assert { type: "json" };
import { commonOpts, fileIncludePlugin, gitHashPlugin, gitRemotePlugin, globPlugins } from "./common.mjs";
import { BUILD_TIMESTAMP, commonOpts, globPlugins, VERSION, watch } from "./common.mjs";
/**
* @type {esbuild.BuildOptions}
@ -36,22 +33,57 @@ const commonOptions = {
entryPoints: ["browser/Vencord.ts"],
globalName: "Vencord",
format: "iife",
external: ["plugins", "git-hash"],
external: ["plugins", "git-hash", "/assets/*"],
plugins: [
globPlugins,
gitHashPlugin,
gitRemotePlugin,
fileIncludePlugin
globPlugins("web"),
...commonOpts.plugins,
],
target: ["esnext"],
define: {
IS_WEB: "true",
IS_STANDALONE: "true"
IS_EXTENSION: "false",
IS_STANDALONE: "true",
IS_DEV: JSON.stringify(watch),
IS_DISCORD_DESKTOP: "false",
IS_VESKTOP: "false",
IS_UPDATER_DISABLED: "true",
VERSION: JSON.stringify(VERSION),
BUILD_TIMESTAMP,
}
};
const MonacoWorkerEntryPoints = [
"vs/language/css/css.worker.js",
"vs/editor/editor.worker.js"
];
const RnNoiseFiles = [
"dist/rnnoise.wasm",
"dist/rnnoise_simd.wasm",
"dist/rnnoise/workletProcessor.js",
"LICENSE"
];
await Promise.all(
[
esbuild.build({
entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`),
bundle: true,
minify: true,
format: "iife",
outbase: "node_modules/monaco-editor/esm/",
outdir: "dist/monaco"
}),
esbuild.build({
entryPoints: ["browser/monaco.ts"],
bundle: true,
minify: true,
format: "iife",
outfile: "dist/monaco/index.js",
loader: {
".ttf": "file"
}
}),
esbuild.build({
...commonOptions,
outfile: "dist/browser.js",
@ -59,32 +91,115 @@ await Promise.all(
}),
esbuild.build({
...commonOptions,
outfile: "dist/extension.js",
define: {
...commonOptions?.define,
IS_EXTENSION: "true",
},
footer: { js: "//# sourceURL=VencordWeb" },
}),
esbuild.build({
...commonOptions,
inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])],
define: {
...(commonOptions?.define),
window: "unsafeWindow",
},
outfile: "dist/Vencord.user.js",
banner: {
js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", PackageJSON.version)
js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${VERSION}.${new Date().getTime()}`)
},
footer: {
// UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local
js: "Object.defineProperty(window,'Vencord',{get:()=>Vencord});"
js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});"
},
})
]
);
zip({
dist: {
"Vencord.js": readFileSync("dist/browser.js")
},
...Object.fromEntries(await Promise.all(["background.js", "content.js", "manifest.json"].map(async f => [
f,
await readFile(join("browser", f))
]))),
}, {}, (err, data) => {
if (err) {
console.error(err);
process.exitCode = 1;
} else {
writeFileSync("dist/extension.zip", data);
console.info("Extension written to dist/extension.zip");
/**
* @type {(dir: string) => Promise<string[]>}
*/
async function globDir(dir) {
const files = [];
for (const child of await readdir(dir, { withFileTypes: true })) {
const p = join(dir, child.name);
if (child.isDirectory())
files.push(...await globDir(p));
else
files.push(p);
}
return files;
}
/**
* @type {(dir: string, basePath?: string) => Promise<Record<string, string>>}
*/
async function loadDir(dir, basePath = "") {
const files = await globDir(dir);
return Object.fromEntries(await Promise.all(files.map(async f => [f.slice(basePath.length), await readFile(f)])));
}
/**
* @type {(target: string, files: string[]) => Promise<void>}
*/
async function buildExtension(target, files) {
const entries = {
"dist/Vencord.js": await readFile("dist/extension.js"),
"dist/Vencord.css": await readFile("dist/extension.css"),
...await loadDir("dist/monaco"),
...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file =>
[`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)]
))),
...Object.fromEntries(await Promise.all(files.map(async f => {
let content = await readFile(join("browser", f));
if (f.startsWith("manifest")) {
const json = JSON.parse(content.toString("utf-8"));
json.version = VERSION;
content = new TextEncoder().encode(JSON.stringify(json));
}
return [
f.startsWith("manifest") ? "manifest.json" : f,
content
];
}))),
};
await rm(target, { recursive: true, force: true });
await Promise.all(Object.entries(entries).map(async ([file, content]) => {
const dest = join("dist", target, file);
const parentDirectory = join(dest, "..");
await mkdir(parentDirectory, { recursive: true });
await writeFile(dest, content);
}));
console.info("Unpacked Extension written to dist/" + target);
}
const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content => {
const cssRuntime = `
;document.addEventListener("DOMContentLoaded", () => document.documentElement.appendChild(
Object.assign(document.createElement("style"), {
textContent: \`${content.replaceAll("`", "\\`")}\`,
id: "vencord-css-core"
})
), { once: true });
`;
return appendFile("dist/Vencord.user.js", cssRuntime);
});
await Promise.all([
appendCssRuntime,
buildExtension("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"]),
buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]),
]);
Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension-chrome.zip");
console.info("Packed Chromium Extension written to dist/extension-chrome.zip");
Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip");
console.info("Packed Firefox Extension written to dist/extension-firefox.zip");

@ -16,19 +16,46 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import "../suppressExperimentalWarnings.js";
import "../checkNodeVersion.js";
import { exec, execSync } from "child_process";
import esbuild from "esbuild";
import { existsSync } from "fs";
import { readdir, readFile } from "fs/promises";
import { join } from "path";
import { constants as FsConstants, readFileSync } from "fs";
import { access, readdir, readFile } from "fs/promises";
import { join, relative } from "path";
import { promisify } from "util";
// wtf is this assert syntax
import PackageJSON from "../../package.json" assert { type: "json" };
import { getPluginTarget } from "../utils.mjs";
export const VERSION = PackageJSON.version;
// https://reproducible-builds.org/docs/source-date-epoch/
export const BUILD_TIMESTAMP = Number(process.env.SOURCE_DATE_EPOCH) || Date.now();
export const watch = process.argv.includes("--watch");
export const isStandalone = JSON.stringify(process.argv.includes("--standalone"));
export const updaterDisabled = JSON.stringify(process.argv.includes("--disable-updater"));
export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
export const banner = {
js: `
// Vencord ${gitHash}
// Standalone: ${isStandalone}
// Platform: ${isStandalone === "false" ? process.platform : "Universal"}
// Updater disabled: ${updaterDisabled}
`.trim()
};
const isWeb = process.argv.slice(0, 2).some(f => f.endsWith("buildWeb.mjs"));
export function existsAsync(path) {
return access(path, FsConstants.F_OK)
.then(() => true)
.catch(() => false);
}
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294
/**
* @type {esbuild.Plugin}
* @type {import("esbuild").Plugin}
*/
export const makeAllPackagesExternalPlugin = {
name: "make-all-packages-external",
@ -39,9 +66,9 @@ export const makeAllPackagesExternalPlugin = {
};
/**
* @type {esbuild.Plugin}
* @type {(kind: "web" | "discordDesktop" | "vencordDesktop") => import("esbuild").Plugin}
*/
export const globPlugins = {
export const globPlugins = kind => ({
name: "glob-plugins",
setup: build => {
const filter = /^~plugins$/;
@ -53,19 +80,28 @@ export const globPlugins = {
});
build.onLoad({ filter, namespace: "import-plugins" }, async () => {
const pluginDirs = ["plugins", "userplugins"];
const pluginDirs = ["plugins/_api", "plugins/_core", "plugins", "userplugins"];
let code = "";
let plugins = "\n";
let i = 0;
for (const dir of pluginDirs) {
if (!existsSync(`./src/${dir}`)) continue;
if (!await existsAsync(`./src/${dir}`)) continue;
const files = await readdir(`./src/${dir}`);
for (const file of files) {
if (file === "index.ts") {
continue;
if (file.startsWith("_") || file.startsWith(".")) continue;
if (file === "index.ts") continue;
const target = getPluginTarget(file);
if (target) {
if (target === "dev" && !watch) continue;
if (target === "web" && kind === "discordDesktop") continue;
if (target === "desktop" && kind === "web") continue;
if (target === "discordDesktop" && kind !== "discordDesktop") continue;
if (target === "vencordDesktop" && kind !== "vencordDesktop") continue;
}
const mod = `p${i}`;
code += `import ${mod} from "./${dir}/${file.replace(/.tsx?$/, "")}";\n`;
code += `import ${mod} from "./${dir}/${file.replace(/\.tsx?$/, "")}";\n`;
plugins += `[${mod}.name]:${mod},\n`;
i++;
}
@ -77,11 +113,10 @@ export const globPlugins = {
};
});
}
};
});
export const gitHash = execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
/**
* @type {esbuild.Plugin}
* @type {import("esbuild").Plugin}
*/
export const gitHashPlugin = {
name: "git-hash-plugin",
@ -97,7 +132,7 @@ export const gitHashPlugin = {
};
/**
* @type {esbuild.Plugin}
* @type {import("esbuild").Plugin}
*/
export const gitRemotePlugin = {
name: "git-remote-plugin",
@ -107,11 +142,14 @@ export const gitRemotePlugin = {
namespace: "git-remote", path: args.path
}));
build.onLoad({ filter, namespace: "git-remote" }, async () => {
const res = await promisify(exec)("git remote get-url origin", { encoding: "utf-8" });
const remote = res.stdout.trim()
.replace("https://github.com/", "")
.replace("git@github.com:", "")
.replace(/.git$/, "");
let remote = process.env.VENCORD_REMOTE;
if (!remote) {
const res = await promisify(exec)("git remote get-url origin", { encoding: "utf-8" });
remote = res.stdout.trim()
.replace("https://github.com/", "")
.replace("git@github.com:", "")
.replace(/.git$/, "");
}
return { contents: `export default "${remote}"` };
});
@ -119,7 +157,7 @@ export const gitRemotePlugin = {
};
/**
* @type {esbuild.Plugin}
* @type {import("esbuild").Plugin}
*/
export const fileIncludePlugin = {
name: "file-include-plugin",
@ -141,8 +179,33 @@ export const fileIncludePlugin = {
}
};
const styleModule = readFileSync("./scripts/build/module/style.js", "utf-8");
/**
* @type {esbuild.BuildOptions}
* @type {import("esbuild").Plugin}
*/
export const stylePlugin = {
name: "style-plugin",
setup: ({ onResolve, onLoad }) => {
onResolve({ filter: /\.css\?managed$/, namespace: "file" }, ({ path, resolveDir }) => ({
path: relative(process.cwd(), join(resolveDir, path.replace("?managed", ""))),
namespace: "managed-style",
}));
onLoad({ filter: /\.css$/, namespace: "managed-style" }, async ({ path }) => {
const css = await readFile(path, "utf-8");
const name = relative(process.cwd(), path).replaceAll("\\", "/");
return {
loader: "js",
contents: styleModule
.replaceAll("STYLE_SOURCE", JSON.stringify(css))
.replaceAll("STYLE_NAME", JSON.stringify(name))
};
});
}
};
/**
* @type {import("esbuild").BuildOptions}
*/
export const commonOpts = {
logLevel: "info",
@ -151,6 +214,12 @@ export const commonOpts = {
minify: !watch,
sourcemap: watch ? "inline" : "",
legalComments: "linked",
plugins: [fileIncludePlugin, gitHashPlugin, gitRemotePlugin],
external: ["~plugins", "~git-hash", "~git-remote"]
banner,
plugins: [fileIncludePlugin, gitHashPlugin, gitRemotePlugin, stylePlugin],
external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"],
inject: ["./scripts/build/inject/react.mjs"],
jsxFactory: "VencordCreateElement",
jsxFragment: "VencordFragment",
// Work around https://github.com/evanw/esbuild/issues/2460
tsconfig: "./scripts/build/tsconfig.esbuild.json"
};

@ -0,0 +1,21 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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/>.
*/
export const VencordFragment = /* #__PURE__*/ Symbol.for("react.fragment");
export let VencordCreateElement =
(...args) => (VencordCreateElement = Vencord.Webpack.Common.React.createElement)(...args);

@ -16,18 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Devs } from "../utils/constants";
import definePlugin from "../utils/types";
export default definePlugin({
name: "SilentTyping",
authors: [Devs.Ven],
description: "Hide that you are typing",
patches: [{
find: "startTyping:",
replacement: {
match: /startTyping:.+?,stop/,
replace: "startTyping:()=>{},stop"
}
}]
(window.VencordStyles ??= new Map()).set(STYLE_NAME, {
name: STYLE_NAME,
source: STYLE_SOURCE,
classNames: {},
dom: null,
});
export default STYLE_NAME;

@ -0,0 +1,7 @@
// Work around https://github.com/evanw/esbuild/issues/2460
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"jsx": "react"
}
}

@ -0,0 +1,20 @@
/*
* 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/>.
*/
if (Number(process.versions.node.split(".")[0]) < 18)
throw `Your node version (${process.version}) is too old, please update to v18 or higher https://nodejs.org/en/download/`;

@ -0,0 +1,230 @@
/*
* 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 { Dirent, readdirSync, readFileSync, writeFileSync } from "fs";
import { access, readFile } from "fs/promises";
import { join, sep } from "path";
import { normalize as posixNormalize, sep as posixSep } from "path/posix";
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
import { getPluginTarget } from "./utils.mjs";
interface Dev {
name: string;
id: string;
}
interface PluginData {
name: string;
description: string;
tags: string[];
authors: Dev[];
dependencies: string[];
hasPatches: boolean;
hasCommands: boolean;
required: boolean;
enabledByDefault: boolean;
target: "discordDesktop" | "vencordDesktop" | "web" | "dev";
filePath: string;
}
const devs = {} as Record<string, Dev>;
function getName(node: NamedDeclaration) {
return node.name && isIdentifier(node.name) ? node.name.text : undefined;
}
function hasName(node: NamedDeclaration, name: string) {
return getName(node) === name;
}
function getObjectProp(node: ObjectLiteralExpression, name: string) {
const prop = node.properties.find(p => hasName(p, name));
if (prop && isPropertyAssignment(prop)) return prop.initializer;
return prop;
}
function parseDevs() {
const file = createSourceFile("constants.ts", readFileSync("src/utils/constants.ts", "utf8"), ScriptTarget.Latest);
for (const child of file.getChildAt(0).getChildren()) {
if (!isVariableStatement(child)) continue;
const devsDeclaration = child.declarationList.declarations.find(d => hasName(d, "Devs"));
if (!devsDeclaration?.initializer || !isCallExpression(devsDeclaration.initializer)) continue;
const value = devsDeclaration.initializer.arguments[0];
if (!isSatisfiesExpression(value) || !isObjectLiteralExpression(value.expression)) throw new Error("Failed to parse devs: not an object literal");
for (const prop of value.expression.properties) {
const name = (prop.name as Identifier).text;
const value = isPropertyAssignment(prop) ? prop.initializer : prop;
if (!isObjectLiteralExpression(value)) throw new Error(`Failed to parse devs: ${name} is not an object literal`);
devs[name] = {
name: (getObjectProp(value, "name") as StringLiteral).text,
id: (getObjectProp(value, "id") as BigIntLiteral).text.slice(0, -1)
};
}
return;
}
throw new Error("Could not find Devs constant");
}
async function parseFile(fileName: string) {
const file = createSourceFile(fileName, await readFile(fileName, "utf8"), ScriptTarget.Latest);
const fail = (reason: string) => {
return new Error(`Invalid plugin ${fileName}, because ${reason}`);
};
for (const node of file.getChildAt(0).getChildren()) {
if (!isExportAssignment(node) || !isCallExpression(node.expression)) continue;
const call = node.expression;
if (!isIdentifier(call.expression) || call.expression.text !== "definePlugin") continue;
const pluginObj = node.expression.arguments[0];
if (!isObjectLiteralExpression(pluginObj)) throw fail("no object literal passed to definePlugin");
const data = {
hasPatches: false,
hasCommands: false,
enabledByDefault: false,
required: false,
tags: [] as string[]
} as PluginData;
for (const prop of pluginObj.properties) {
const key = getName(prop);
const value = isPropertyAssignment(prop) ? prop.initializer : prop;
switch (key) {
case "name":
case "description":
if (!isStringLiteral(value)) throw fail(`${key} is not a string literal`);
data[key] = value.text;
break;
case "patches":
data.hasPatches = true;
break;
case "commands":
data.hasCommands = true;
break;
case "authors":
if (!isArrayLiteralExpression(value)) throw fail("authors is not an array literal");
data.authors = value.elements.map(e => {
if (!isPropertyAccessExpression(e)) throw fail("authors array contains non-property access expressions");
const d = devs[getName(e)!];
if (!d) throw fail(`couldn't look up author ${getName(e)}`);
return d;
});
break;
case "tags":
if (!isArrayLiteralExpression(value)) throw fail("tags is not an array literal");
data.tags = value.elements.map(e => {
if (!isStringLiteral(e)) throw fail("tags array contains non-string literals");
return e.text;
});
break;
case "dependencies":
if (!isArrayLiteralExpression(value)) throw fail("dependencies is not an array literal");
const { elements } = value;
if (elements.some(e => !isStringLiteral(e))) throw fail("dependencies array contains non-string elements");
data.dependencies = (elements as NodeArray<StringLiteral>).map(e => e.text);
break;
case "required":
case "enabledByDefault":
data[key] = value.kind === SyntaxKind.TrueKeyword;
break;
}
}
if (!data.name || !data.description || !data.authors) throw fail("name, description or authors are missing");
const target = getPluginTarget(fileName);
if (target) {
if (!["web", "discordDesktop", "vencordDesktop", "desktop", "dev"].includes(target)) throw fail(`invalid target ${target}`);
data.target = target as any;
}
data.filePath = posixNormalize(fileName)
.split(sep)
.join(posixSep)
.replace(/\/index\.([jt]sx?)$/, "")
.replace(/^src\/plugins\//, "");
let readme = "";
try {
readme = readFileSync(join(fileName, "..", "README.md"), "utf-8");
} catch { }
return [data, readme] as const;
}
throw fail("no default export called 'definePlugin' found");
}
async function getEntryPoint(dir: string, dirent: Dirent) {
const base = join(dir, dirent.name);
if (!dirent.isDirectory()) return base;
for (const name of ["index.ts", "index.tsx"]) {
const full = join(base, name);
try {
await access(full);
return full;
} catch { }
}
throw new Error(`${dirent.name}: Couldn't find entry point`);
}
function isPluginFile({ name }: { name: string; }) {
if (name === "index.ts") return false;
return !name.startsWith("_") && !name.startsWith(".");
}
(async () => {
parseDevs();
const plugins = [] as PluginData[];
const readmes = {} as Record<string, string>;
await Promise.all(["src/plugins", "src/plugins/_core"].flatMap(dir =>
readdirSync(dir, { withFileTypes: true })
.filter(isPluginFile)
.map(async dirent => {
const [data, readme] = await parseFile(await getEntryPoint(dir, dirent));
plugins.push(data);
if (readme) readmes[data.name] = readme;
})
));
const data = JSON.stringify(plugins);
if (process.argv.length > 3) {
writeFileSync(process.argv[2], data);
writeFileSync(process.argv[3], JSON.stringify(readmes));
} else {
console.log(data);
}
})();

304
scripts/generateReport.ts Normal file

@ -0,0 +1,304 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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/>.
*/
// eslint-disable-next-line spaced-comment
/// <reference types="../src/globals" />
// eslint-disable-next-line spaced-comment
/// <reference types="../src/modules" />
import { readFileSync } from "fs";
import pup, { JSHandle } from "puppeteer-core";
for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) {
if (!process.env[variable]) {
console.error(`Missing environment variable ${variable}`);
process.exit(1);
}
}
const CANARY = process.env.USE_CANARY === "true";
const browser = await pup.launch({
headless: true,
executablePath: process.env.CHROMIUM_BIN
});
const page = await browser.newPage();
await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36");
function maybeGetError(handle: JSHandle) {
return (handle as JSHandle<Error>)?.getProperty("message")
.then(m => m.jsonValue());
}
const report = {
badPatches: [] as {
plugin: string;
type: string;
id: string;
match: string;
error?: string;
}[],
badStarts: [] as {
plugin: string;
error: string;
}[],
otherErrors: [] as string[]
};
const IGNORED_DISCORD_ERRORS = [
"KeybindStore: Looking for callback action",
"Unable to process domain list delta: Client revision number is null",
"Downloading the full bad domains file",
/\[GatewaySocket\].{0,110}Cannot access '/
] as Array<string | RegExp>;
function toCodeBlock(s: string) {
s = s.replace(/```/g, "`\u200B`\u200B`");
return "```" + s + " ```";
}
async function printReport() {
console.log("# Vencord Report" + (CANARY ? " (Canary)" : ""));
console.log();
console.log("## Bad Patches");
report.badPatches.forEach(p => {
console.log(`- ${p.plugin} (${p.type})`);
console.log(` - ID: \`${p.id}\``);
console.log(` - Match: ${toCodeBlock(p.match)}`);
if (p.error) console.log(` - Error: ${toCodeBlock(p.error)}`);
});
console.log();
console.log("## Bad Starts");
report.badStarts.forEach(p => {
console.log(`- ${p.plugin}`);
console.log(` - Error: ${toCodeBlock(p.error)}`);
});
report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex)));
console.log("## Discord Errors");
report.otherErrors.forEach(e => {
console.log(`- ${toCodeBlock(e)}`);
});
if (process.env.DISCORD_WEBHOOK) {
// this code was written almost entirely by Copilot xD
await fetch(process.env.DISCORD_WEBHOOK, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
description: "Here's the latest Vencord Report!",
username: "Vencord Reporter" + (CANARY ? " (Canary)" : ""),
avatar_url: "https://cdn.discordapp.com/icons/1015060230222131221/f0204a918c6c9c9a43195997e97d8adf.webp",
embeds: [
{
title: "Bad Patches",
description: report.badPatches.map(p => {
const lines = [
`**__${p.plugin} (${p.type}):__**`,
`ID: \`${p.id}\``,
`Match: ${toCodeBlock(p.match)}`
];
if (p.error) lines.push(`Error: ${toCodeBlock(p.error)}`);
return lines.join("\n");
}).join("\n\n") || "None",
color: report.badPatches.length ? 0xff0000 : 0x00ff00
},
{
title: "Bad Starts",
description: report.badStarts.map(p => {
const lines = [
`**__${p.plugin}:__**`,
toCodeBlock(p.error)
];
return lines.join("\n");
}
).join("\n\n") || "None",
color: report.badStarts.length ? 0xff0000 : 0x00ff00
},
{
title: "Discord Errors",
description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n")) : "None",
color: report.otherErrors.length ? 0xff0000 : 0x00ff00
}
]
})
}).then(res => {
if (!res.ok) console.error(`Webhook failed with status ${res.status}`);
else console.error("Posted to Discord Webhook successfully");
});
}
}
page.on("console", async e => {
const level = e.type();
const args = e.args();
const firstArg = (await args[0]?.jsonValue());
if (firstArg === "PUPPETEER_TEST_DONE_SIGNAL") {
await browser.close();
await printReport();
process.exit();
}
const isVencord = (await args[0]?.jsonValue()) === "[Vencord]";
const isDebug = (await args[0]?.jsonValue()) === "[PUP_DEBUG]";
if (isVencord) {
// make ci fail
process.exitCode = 1;
const jsonArgs = await Promise.all(args.map(a => a.jsonValue()));
const [, tag, message] = jsonArgs;
const cause = await maybeGetError(args[3]);
switch (tag) {
case "WebpackInterceptor:":
const [, plugin, type, id, regex] = (message as string).match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!;
report.badPatches.push({
plugin,
type,
id,
match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"),
error: cause
});
break;
case "PluginManager:":
const [, name] = (message as string).match(/Failed to start (.+)/)!;
report.badStarts.push({
plugin: name,
error: cause
});
break;
}
} else if (isDebug) {
console.error(e.text());
} else if (level === "error") {
const text = await Promise.all(
e.args().map(async a => {
try {
return await maybeGetError(a) || await a.jsonValue();
} catch (e) {
return a.toString();
}
})
).then(a => a.join(" ").trim());
if (text.length && !text.startsWith("Failed to load resource: the server responded with a status of")) {
console.error("Got unexpected error", text);
report.otherErrors.push(text);
}
}
});
page.on("error", e => console.error("[Error]", e));
page.on("pageerror", e => console.error("[Page Error]", e));
await page.setBypassCSP(true);
function runTime(token: string) {
console.error("[PUP_DEBUG]", "Starting test...");
try {
// spoof languages to not be suspicious
Object.defineProperty(navigator, "languages", {
get: function () {
return ["en-US", "en"];
},
});
// Monkey patch Logger to not log with custom css
// @ts-ignore
Vencord.Util.Logger.prototype._log = function (level, levelColor, args) {
if (level === "warn" || level === "error")
console[level]("[Vencord]", this.name + ":", ...args);
};
// force enable all plugins and patches
Vencord.Plugins.patches.length = 0;
Object.values(Vencord.Plugins.plugins).forEach(p => {
// Needs native server to run
if (p.name === "WebRichPresence (arRPC)") return;
Vencord.Settings.plugins[p.name].enabled = true;
p.patches?.forEach(patch => {
patch.plugin = p.name;
delete patch.predicate;
if (!Array.isArray(patch.replacement))
patch.replacement = [patch.replacement];
Vencord.Plugins.patches.push(patch);
});
});
Vencord.Webpack.waitFor(
"loginToken",
m => {
console.error("[PUP_DEBUG]", "Logging in with token...");
m.loginToken(token);
}
);
// force load all chunks
Vencord.Webpack.onceReady.then(() => setTimeout(async () => {
console.error("[PUP_DEBUG]", "Webpack is ready!");
const { wreq } = Vencord.Webpack;
console.error("[PUP_DEBUG]", "Loading all chunks...");
const ids = Function("return" + wreq.u.toString().match(/(?<=\()\{.+?\}/s)![0])();
for (const id in ids) {
const isWasm = await fetch(wreq.p + wreq.u(id))
.then(r => r.text())
.then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push"));
if (!isWasm)
await wreq.e(id as any);
await new Promise(r => setTimeout(r, 150));
}
console.error("[PUP_DEBUG]", "Finished loading chunks!");
for (const patch of Vencord.Plugins.patches) {
if (!patch.all) {
new Vencord.Util.Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`);
}
}
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
}, 1000));
} catch (e) {
console.error("[PUP_DEBUG]", "A fatal error occurred");
console.error("[PUP_DEBUG]", e);
process.exit(1);
}
}
await page.evaluateOnNewDocument(`
${readFileSync("./dist/browser.js", "utf-8")}
;(${runTime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)});
`);
await page.goto(CANARY ? "https://canary.discord.com/login" : "https://discord.com/login");

3
scripts/header-new.txt Normal file

@ -0,0 +1,3 @@
Vencord, a Discord client mod
Copyright (c) {year} {author}
SPDX-License-Identifier: GPL-3.0-or-later

17
scripts/header-old.txt Normal file

@ -0,0 +1,17 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) {year} {author}
*
* 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/>.
*/

@ -1,341 +0,0 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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/>.
*/
const path = require("path");
const readline = require("readline");
const fs = require("fs");
const menu = require("console-menu");
const BRANCH_NAMES = [
"Discord",
"DiscordPTB",
"DiscordCanary",
"DiscordDevelopment",
"discord",
"discordptb",
"discordcanary",
"discorddevelopment",
"discord-ptb",
"discord-canary",
"discord-development",
// Flatpak
"com.discordapp.Discord",
"com.discordapp.DiscordPTB",
"com.discordapp.DiscordCanary",
"com.discordapp.DiscordDevelopment",
];
const MACOS_DISCORD_DIRS = [
"Discord.app",
"Discord PTB.app",
"Discord Canary.app",
"Discord Development.app",
];
if (process.platform === "linux" && process.env.SUDO_USER) {
process.env.HOME = fs
.readFileSync("/etc/passwd", "utf-8")
.match(new RegExp(`^${process.env.SUDO_USER}.+$`, "m"))[0]
.split(":")[5];
}
const LINUX_DISCORD_DIRS = [
"/usr/share",
"/usr/lib64",
"/opt",
`${process.env.HOME}/.local/share`,
"/var/lib/flatpak/app",
`${process.env.HOME}/.local/share/flatpak/app`,
];
const FLATPAK_NAME_MAPPING = {
DiscordCanary: "discord-canary",
DiscordPTB: "discord-ptb",
DiscordDevelopment: "discord-development",
Discord: "discord",
};
const ENTRYPOINT = path
.join(process.cwd(), "dist", "patcher.js")
.replace(/\\/g, "/");
function question(question) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false,
});
return new Promise(resolve => {
rl.question(question, answer => {
rl.close();
resolve(answer);
});
});
}
async function getMenuItem(installations) {
const menuItems = installations.map(info => ({
title: info.patched ? "[MODIFIED] " + info.location : info.location,
info,
}));
const result = await menu(
[
...menuItems,
{ title: "Specify custom path", info: "custom" },
{ title: "Exit without patching", exit: true }
],
{
header: "Select a Discord installation to patch:",
border: true,
helpMessage:
"Use the up/down arrow keys to select an option. " +
"Press ENTER to confirm.",
}
);
if (!result || !result.info || result.exit) {
console.log("No installation selected.");
process.exit(0);
}
if (result.info === "custom") {
const customPath = await question("Please enter the path: ");
if (!customPath || !fs.existsSync(customPath)) {
console.log("No such Path or not specifed.");
process.exit();
}
const resourceDir = path.join(customPath, "resources");
if (!fs.existsSync(path.join(resourceDir, "app.asar"))) {
console.log("Unsupported Install. resources/app.asar not found");
process.exit();
}
const appDir = path.join(resourceDir, "app");
result.info = {
branch: "unknown",
patched: fs.existsSync(appDir),
location: customPath,
versions: [{
path: appDir,
name: null
}],
arch: process.platform === "linux" ? "linux" : "win32",
isFlatpak: false,
};
}
if (result.info.patched) {
const answer = await question(
"This installation has already been modified. Overwrite? [Y/n]: "
);
if (!["y", "yes", "yeah", ""].includes(answer.toLowerCase())) {
console.log("Not patching.");
process.exit(0);
}
}
return result.info;
}
function getWindowsDirs() {
const dirs = [];
for (const dir of fs.readdirSync(process.env.LOCALAPPDATA)) {
if (!BRANCH_NAMES.includes(dir)) continue;
const location = path.join(process.env.LOCALAPPDATA, dir);
if (!fs.statSync(location).isDirectory()) continue;
const appDirs = fs
.readdirSync(location, { withFileTypes: true })
.filter(file => file.isDirectory())
.filter(file => file.name.startsWith("app-"))
.map(file => path.join(location, file.name));
const versions = [];
let patched = false;
for (const fqAppDir of appDirs) {
const resourceDir = path.join(fqAppDir, "resources");
if (!fs.existsSync(path.join(resourceDir, "app.asar"))) {
continue;
}
const appDir = path.join(resourceDir, "app");
if (fs.existsSync(appDir)) {
patched = true;
}
versions.push({
path: appDir,
name: /app-([0-9.]+)/.exec(fqAppDir)[1],
});
}
if (appDirs.length) {
dirs.push({
branch: dir,
patched,
location,
versions,
arch: "win32",
flatpak: false,
});
}
}
return dirs;
}
function getDarwinDirs() {
const dirs = [];
for (const dir of fs.readdirSync("/Applications")) {
if (!MACOS_DISCORD_DIRS.includes(dir)) continue;
const location = path.join("/Applications", dir, "Contents");
if (!fs.existsSync(location)) continue;
if (!fs.statSync(location).isDirectory()) continue;
const appDirs = fs
.readdirSync(location, { withFileTypes: true })
.filter(file => file.isDirectory())
.filter(file => file.name.startsWith("Resources"))
.map(file => path.join(location, file.name));
const versions = [];
let patched = false;
for (const resourceDir of appDirs) {
if (!fs.existsSync(path.join(resourceDir, "app.asar"))) {
continue;
}
const appDir = path.join(resourceDir, "app");
if (fs.existsSync(appDir)) {
patched = true;
}
versions.push({
path: appDir,
name: null, // MacOS installs have no version number
});
}
if (appDirs.length) {
dirs.push({
branch: dir,
patched,
location,
versions,
arch: "win32",
});
}
}
return dirs;
}
function getLinuxDirs() {
const dirs = [];
for (const dir of LINUX_DISCORD_DIRS) {
if (!fs.existsSync(dir)) continue;
for (const branch of fs.readdirSync(dir)) {
if (!BRANCH_NAMES.includes(branch)) continue;
const location = path.join(dir, branch);
if (!fs.statSync(location).isDirectory()) continue;
const isFlatpak = location.includes("/flatpak/");
let appDirs = [];
if (isFlatpak) {
const fqDir = path.join(location, "current", "active", "files");
if (!/com\.discordapp\.(\w+)\//.test(fqDir)) continue;
const branchName = /com\.discordapp\.(\w+)\//.exec(fqDir)[1];
if (!Object.keys(FLATPAK_NAME_MAPPING).includes(branchName)) {
continue;
}
const appDir = path.join(
fqDir,
FLATPAK_NAME_MAPPING[branchName]
);
if (!fs.existsSync(appDir)) continue;
if (!fs.statSync(appDir).isDirectory()) continue;
const resourceDir = path.join(appDir, "resources");
appDirs.push(resourceDir);
} else {
appDirs = fs
.readdirSync(location, { withFileTypes: true })
.filter(file => file.isDirectory())
.filter(
file =>
file.name.startsWith("app-") ||
file.name === "resources"
)
.map(file => path.join(location, file.name));
}
const versions = [];
let patched = false;
for (const resourceDir of appDirs) {
if (!fs.existsSync(path.join(resourceDir, "app.asar"))) {
continue;
}
const appDir = path.join(resourceDir, "app");
if (fs.existsSync(appDir)) {
patched = true;
}
const version = /app-([0-9.]+)/.exec(resourceDir);
versions.push({
path: appDir,
name: version && version.length > 1 ? version[1] : null,
});
}
if (appDirs.length) {
dirs.push({
branch,
patched,
location,
versions,
arch: "linux",
isFlatpak,
});
}
}
}
return dirs;
}
module.exports = {
BRANCH_NAMES,
MACOS_DISCORD_DIRS,
LINUX_DISCORD_DIRS,
FLATPAK_NAME_MAPPING,
ENTRYPOINT,
question,
getMenuItem,
getWindowsDirs,
getDarwinDirs,
getLinuxDirs,
};

@ -1,130 +0,0 @@
#!/usr/bin/node
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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/>.
*/
const path = require("path");
const fs = require("fs");
const { execSync } = require("child_process");
console.log("\nVencord Installer\n");
if (!fs.existsSync(path.join(process.cwd(), "node_modules"))) {
console.log("You need to install dependencies first. Run:", "pnpm install --frozen-lockfile");
process.exit(1);
}
if (!fs.existsSync(path.join(process.cwd(), "dist", "patcher.js"))) {
console.log("You need to build the project first. Run:", "pnpm build");
process.exit(1);
}
const {
getMenuItem,
getWindowsDirs,
getDarwinDirs,
getLinuxDirs,
ENTRYPOINT,
} = require("./common");
switch (process.platform) {
case "win32":
install(getWindowsDirs());
break;
case "darwin":
install(getDarwinDirs());
break;
case "linux":
install(getLinuxDirs());
break;
default:
console.log("Unknown OS");
break;
}
async function install(installations) {
const selected = await getMenuItem(installations);
// Attempt to give flatpak perms
if (selected.isFlatpak) {
try {
const { branch } = selected;
const cwd = process.cwd();
const globalCmd = `flatpak override ${branch} --filesystem=${cwd}`;
const userCmd = `flatpak override --user ${branch} --filesystem=${cwd}`;
const cmd = selected.location.startsWith("/home")
? userCmd
: globalCmd;
execSync(cmd);
console.log("Successfully gave write perms to Discord Flatpak.");
} catch (e) {
console.log("Failed to give write perms to Discord Flatpak.");
console.log(
"Try running this script as an administrator:",
"sudo pnpm inject"
);
process.exit(1);
}
}
for (const version of selected.versions) {
const dir = version.path;
// Check if we have write perms to the install directory...
try {
fs.accessSync(selected.location, fs.constants.W_OK);
} catch (e) {
console.error("No write access to", selected.location);
console.error(
"Try running this script as an administrator:",
"sudo pnpm inject"
);
process.exit(1);
}
if (fs.existsSync(dir) && fs.lstatSync(dir).isDirectory()) {
fs.rmSync(dir, { recursive: true });
}
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(
path.join(dir, "index.js"),
`require("${ENTRYPOINT}");`
);
fs.writeFileSync(
path.join(dir, "package.json"),
JSON.stringify({
name: "discord",
main: "index.js",
})
);
const requiredFiles = ["index.js", "package.json"];
if (requiredFiles.every(f => fs.existsSync(path.join(dir, f)))) {
console.log(
"Successfully patched",
version.name
? `${selected.branch} ${version.name}`
: selected.branch
);
} else {
console.log("Failed to patch", dir);
console.log("Files in directory:", fs.readdirSync(dir));
}
}
}

@ -1,78 +0,0 @@
#!/usr/bin/node
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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/>.
*/
const path = require("path");
const fs = require("fs");
console.log("\nVencord Uninstaller\n");
if (!fs.existsSync(path.join(process.cwd(), "node_modules"))) {
console.log("You need to install dependencies first. Run:", "pnpm install --frozen-lockfile");
process.exit(1);
}
const {
getMenuItem,
getWindowsDirs,
getDarwinDirs,
getLinuxDirs,
} = require("./common");
switch (process.platform) {
case "win32":
uninstall(getWindowsDirs());
break;
case "darwin":
uninstall(getDarwinDirs());
break;
case "linux":
uninstall(getLinuxDirs());
break;
default:
console.log("Unknown OS");
break;
}
async function uninstall(installations) {
const selected = await getMenuItem(installations);
for (const version of selected.versions) {
const dir = version.path;
// Check if we have write perms to the install directory...
try {
fs.accessSync(selected.location, fs.constants.W_OK);
} catch (e) {
console.error("No write access to", selected.location);
console.error(
"Try running this script as an administrator:",
"sudo pnpm uninject"
);
process.exit(1);
}
if (fs.existsSync(dir)) {
fs.rmSync(dir, { recursive: true });
}
console.log(
"Successfully unpatched",
version.name
? `${selected.branch} ${version.name}`
: selected.branch
);
}
}

128
scripts/runInstaller.mjs Normal file

@ -0,0 +1,128 @@
/*
* 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 "./checkNodeVersion.js";
import { execFileSync, execSync } from "child_process";
import { createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join } from "path";
import { Readable } from "stream";
import { finished } from "stream/promises";
import { fileURLToPath } from "url";
const BASE_URL = "https://github.com/Vencord/Installer/releases/latest/download/";
const INSTALLER_PATH_DARWIN = "VencordInstaller.app/Contents/MacOS/VencordInstaller";
const BASE_DIR = join(dirname(fileURLToPath(import.meta.url)), "..");
const FILE_DIR = join(BASE_DIR, "dist", "Installer");
const ETAG_FILE = join(FILE_DIR, "etag.txt");
function getFilename() {
switch (process.platform) {
case "win32":
return "VencordInstaller.exe";
case "darwin":
return "VencordInstaller.MacOS.zip";
case "linux":
return "VencordInstaller-" + (process.env.WAYLAND_DISPLAY ? "wayland" : "x11");
default:
throw new Error("Unsupported platform: " + process.platform);
}
}
async function ensureBinary() {
const filename = getFilename();
console.log("Downloading " + filename);
mkdirSync(FILE_DIR, { recursive: true });
const downloadName = join(FILE_DIR, filename);
const outputFile = process.platform === "darwin"
? join(FILE_DIR, "VencordInstaller")
: downloadName;
const etag = existsSync(outputFile) && existsSync(ETAG_FILE)
? readFileSync(ETAG_FILE, "utf-8")
: null;
const res = await fetch(BASE_URL + filename, {
headers: {
"User-Agent": "Vencord (https://github.com/Vendicated/Vencord)",
"If-None-Match": etag
}
});
if (res.status === 304) {
console.log("Up to date, not redownloading!");
return outputFile;
}
if (!res.ok)
throw new Error(`Failed to download installer: ${res.status} ${res.statusText}`);
writeFileSync(ETAG_FILE, res.headers.get("etag"));
if (process.platform === "darwin") {
console.log("Unzipping...");
const zip = new Uint8Array(await res.arrayBuffer());
const ff = await import("fflate");
const bytes = ff.unzipSync(zip, {
filter: f => f.name === INSTALLER_PATH_DARWIN
})[INSTALLER_PATH_DARWIN];
writeFileSync(outputFile, bytes, { mode: 0o755 });
console.log("Overriding security policy for installer binary (this is required to run it)");
console.log("xattr might error, that's okay");
const logAndRun = cmd => {
console.log("Running", cmd);
try {
execSync(cmd);
} catch { }
};
logAndRun(`sudo spctl --add '${outputFile}' --label "Vencord Installer"`);
logAndRun(`sudo xattr -d com.apple.quarantine '${outputFile}'`);
} else {
// WHY DOES NODE FETCH RETURN A WEB STREAM OH MY GOD
const body = Readable.fromWeb(res.body);
await finished(body.pipe(createWriteStream(outputFile, {
mode: 0o755,
autoClose: true
})));
}
console.log("Finished downloading!");
return outputFile;
}
const installerBin = await ensureBinary();
console.log("Now running Installer...");
execFileSync(installerBin, {
stdio: "inherit",
env: {
...process.env,
VENCORD_USER_DATA_DIR: BASE_DIR,
VENCORD_DEV_INSTALL: "1"
}
});

30
scripts/utils.mjs Normal file

@ -0,0 +1,30 @@
/*
* 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/>.
*/
/**
* @param {string} filePath
* @returns {string | null}
*/
export function getPluginTarget(filePath) {
const pathParts = filePath.split(/[/\\]/);
if (/^index\.tsx?$/.test(pathParts.at(-1))) pathParts.pop();
const identifier = pathParts.at(-1).replace(/\.tsx?$/, "");
const identiferBits = identifier.split(".");
return identiferBits.length === 1 ? null : identiferBits.at(-1);
}

@ -22,45 +22,121 @@ export * as Util from "./utils";
export * as QuickCss from "./utils/quickCss";
export * as Updater from "./utils/updater";
export * as Webpack from "./webpack";
export { PlainSettings, Settings };
import { popNotice, showNotice } from "./api/Notices";
import { PlainSettings,Settings } from "./api/settings";
import { startAllPlugins } from "./plugins";
export { PlainSettings,Settings };
import "./webpack/patchWebpack";
import "./utils/quickCss";
import "./webpack/patchWebpack";
import { checkForUpdates, UpdateLogger } from "./utils/updater";
import { get as dsGet } from "./api/DataStore";
import { showNotification } from "./api/Notifications";
import { PlainSettings, Settings } from "./api/Settings";
import { patches, PMLogger, startAllPlugins } from "./plugins";
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 { Router } from "./webpack/common";
import { SettingsRouter } from "./webpack/common";
export let Components: any;
async function syncSettings() {
// pre-check for local shared settings
if (
Settings.cloud.authenticated &&
await dsGet("Vencord_cloudSecret") === null // this has been enabled due to local settings share or some other bug
) {
// show a notification letting them know and tell them how to fix it
showNotification({
title: "Cloud Integrations",
body: "We've noticed you have cloud integrations enabled in another client! Due to limitations, you will " +
"need to re-authenticate to continue using them. Click here to go to the settings page to do so!",
color: "var(--yellow-360)",
onClick: () => SettingsRouter.open("VencordCloud")
});
return;
}
async function init() {
await onceReady;
startAllPlugins();
Components = await import("./components");
if (!IS_WEB) {
try {
const isOutdated = await checkForUpdates();
if (isOutdated && Settings.notifyAboutUpdates)
setTimeout(() => {
showNotice(
"A Vencord update is available!",
"View Update",
() => {
popNotice();
Router.open("VencordUpdater");
}
);
}, 10000);
} catch (err) {
UpdateLogger.error("Failed to check for updates", err);
if (
Settings.cloud.settingsSync && // if it's enabled
Settings.cloud.authenticated // if cloud integrations are enabled
) {
if (localStorage.Vencord_settingsDirty) {
await putCloudSettings();
delete localStorage.Vencord_settingsDirty;
} else if (await getCloudSettings(false)) { // if we synchronized something (false means no sync)
// we show a notification here instead of allowing getCloudSettings() to show one to declutter the amount of
// potential notifications that might occur. getCloudSettings() will always send a notification regardless if
// there was an error to notify the user, but besides that we only want to show one notification instead of all
// of the possible ones it has (such as when your settings are newer).
showNotification({
title: "Cloud Settings",
body: "Your settings have been updated! Click here to restart to fully apply changes!",
color: "var(--green-360)",
onClick: relaunch
});
}
}
}
async function init() {
await onceReady;
startAllPlugins();
syncSettings();
if (!IS_WEB) {
try {
const isOutdated = await checkForUpdates();
if (!isOutdated) return;
if (Settings.autoUpdate) {
await update();
if (Settings.autoUpdateNotification)
setTimeout(() => showNotification({
title: "Vencord has been updated!",
body: "Click here to restart",
permanent: true,
noPersist: true,
onClick: relaunch
}), 10_000);
return;
}
if (Settings.notifyAboutUpdates)
setTimeout(() => showNotification({
title: "A Vencord update is available!",
body: "Click here to view the update",
permanent: true,
noPersist: true,
onClick() {
SettingsRouter.open("VencordUpdater");
}
}), 10_000);
} catch (err) {
UpdateLogger.error("Failed to check for updates", err);
}
}
if (IS_DEV) {
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.",
"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.",
"\nThe following patches have not been applied:",
"\n\n" + pendingPatches.map(p => `${p.plugin}: ${p.find}`).join("\n")
);
}
}
init();
if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) {
document.addEventListener("DOMContentLoaded", () => {
document.head.append(Object.assign(document.createElement("style"), {
id: "vencord-native-titlebar-style",
textContent: "[class*=titleBar]{display: none!important}"
}));
}, { once: true });
}

@ -1,50 +1,76 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022
*
* 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/>.
*/
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { IpcRenderer, ipcRenderer } from "electron";
import { IpcEvents } from "@utils/IpcEvents";
import { IpcRes } from "@utils/types";
import { ipcRenderer } from "electron";
import { PluginIpcMappings } from "main/ipcPlugins";
import type { UserThemeHeader } from "main/themes";
import IPC_EVENTS from "./utils/IpcEvents";
function assertEventAllowed(event: string) {
if (!(event in IPC_EVENTS)) throw new Error(`Event ${event} not allowed.`);
function invoke<T = any>(event: IpcEvents, ...args: any[]) {
return ipcRenderer.invoke(event, ...args) as Promise<T>;
}
export default {
getVersions: () => process.versions,
ipc: {
send(event: string, ...args: any[]) {
assertEventAllowed(event);
ipcRenderer.send(event, ...args);
},
sendSync<T = any>(event: string, ...args: any[]): T {
assertEventAllowed(event);
return ipcRenderer.sendSync(event, ...args);
},
on(event: string, listener: Parameters<IpcRenderer["on"]>[1]) {
assertEventAllowed(event);
ipcRenderer.on(event, listener);
},
off(event: string, listener: Parameters<IpcRenderer["off"]>[1]) {
assertEventAllowed(event);
ipcRenderer.off(event, listener);
},
invoke<T = any>(event: string, ...args: any[]): Promise<T> {
assertEventAllowed(event);
return ipcRenderer.invoke(event, ...args);
}
export function sendSync<T = any>(event: IpcEvents, ...args: any[]) {
return ipcRenderer.sendSync(event, ...args) as T;
}
const PluginHelpers = {} as Record<string, Record<string, (...args: any[]) => Promise<any>>>;
const pluginIpcMap = sendSync<PluginIpcMappings>(IpcEvents.GET_PLUGIN_IPC_METHOD_MAP);
for (const [plugin, methods] of Object.entries(pluginIpcMap)) {
const map = PluginHelpers[plugin] = {};
for (const [methodName, method] of Object.entries(methods)) {
map[methodName] = (...args: any[]) => invoke(method as IpcEvents, ...args);
}
}
export default {
themes: {
uploadTheme: (fileName: string, fileData: string) => invoke<void>(IpcEvents.UPLOAD_THEME, fileName, fileData),
deleteTheme: (fileName: string) => invoke<void>(IpcEvents.DELETE_THEME, fileName),
getThemesDir: () => invoke<string>(IpcEvents.GET_THEMES_DIR),
getThemesList: () => invoke<Array<UserThemeHeader>>(IpcEvents.GET_THEMES_LIST),
getThemeData: (fileName: string) => invoke<string | undefined>(IpcEvents.GET_THEME_DATA, fileName),
getSystemValues: () => invoke<Record<string, string>>(IpcEvents.GET_THEME_SYSTEM_VALUES),
},
updater: {
getUpdates: () => invoke<IpcRes<Record<"hash" | "author" | "message", string>[]>>(IpcEvents.GET_UPDATES),
update: () => invoke<IpcRes<boolean>>(IpcEvents.UPDATE),
rebuild: () => invoke<IpcRes<boolean>>(IpcEvents.BUILD),
getRepo: () => invoke<IpcRes<string>>(IpcEvents.GET_REPO),
},
settings: {
get: () => sendSync<string>(IpcEvents.GET_SETTINGS),
set: (settings: string) => invoke<void>(IpcEvents.SET_SETTINGS, settings),
getSettingsDir: () => invoke<string>(IpcEvents.GET_SETTINGS_DIR),
},
quickCss: {
get: () => invoke<string>(IpcEvents.GET_QUICK_CSS),
set: (css: string) => invoke<void>(IpcEvents.SET_QUICK_CSS, css),
addChangeListener(cb: (newCss: string) => void) {
ipcRenderer.on(IpcEvents.QUICK_CSS_UPDATE, (_, css) => cb(css));
},
addThemeChangeListener(cb: () => void) {
ipcRenderer.on(IpcEvents.THEME_UPDATE, () => cb());
},
openFile: () => invoke<void>(IpcEvents.OPEN_QUICKCSS),
openEditor: () => invoke<void>(IpcEvents.OPEN_MONACO_EDITOR),
},
native: {
getVersions: () => process.versions as Partial<NodeJS.ProcessVersions>,
openExternal: (url: string) => invoke<void>(IpcEvents.OPEN_EXTERNAL, url)
},
pluginHelpers: PluginHelpers
};

110
src/api/Badges.ts Normal file

@ -0,0 +1,110 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 ErrorBoundary from "@components/ErrorBoundary";
import { User } from "discord-types/general";
import { ComponentType, HTMLProps } from "react";
import Plugins from "~plugins";
export const enum BadgePosition {
START,
END
}
export interface ProfileBadge {
/** The tooltip to show on hover. Required for image badges */
description?: string;
/** Custom component for the badge (tooltip not included) */
component?: ComponentType<ProfileBadge & BadgeUserArgs>;
/** The custom image to use */
image?: string;
link?: string;
/** Action to perform when you click the badge */
onClick?(): void;
/** Should the user display this badge? */
shouldShow?(userInfo: BadgeUserArgs): boolean;
/** Optional props (e.g. style) for the badge, ignored for component badges */
props?: HTMLProps<HTMLImageElement>;
/** Insert at start or end? */
position?: BadgePosition;
/** The badge name to display, Discord uses this. Required for component badges */
key?: string;
}
const Badges = new Set<ProfileBadge>();
/**
* Register a new badge with the Badges API
* @param badge The badge to register
*/
export function addBadge(badge: ProfileBadge) {
badge.component &&= ErrorBoundary.wrap(badge.component, { noop: true });
Badges.add(badge);
}
/**
* Unregister a badge from the Badges API
* @param badge The badge to remove
*/
export function removeBadge(badge: ProfileBadge) {
return Badges.delete(badge);
}
/**
* Inject badges into the profile badges array.
* You probably don't need to use this.
*/
export function _getBadges(args: BadgeUserArgs) {
const badges = [] as ProfileBadge[];
for (const badge of Badges) {
if (!badge.shouldShow || badge.shouldShow(args)) {
badge.position === BadgePosition.START
? badges.unshift({ ...badge, ...args })
: badges.push({ ...badge, ...args });
}
}
const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.user.id);
if (donorBadges) badges.unshift(...donorBadges);
return badges;
}
export interface BadgeUserArgs {
user: User;
profile: Profile;
premiumSince: Date;
premiumGuildSince?: Date;
}
interface ConnectedAccount {
type: string;
id: string;
name: string;
verified: boolean;
}
interface Profile {
connectedAccounts: ConnectedAccount[];
premiumType: number;
premiumSince: string;
premiumGuildSince?: any;
lastFetched: number;
profileFetchFailed: boolean;
application?: any;
}

@ -16,18 +16,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { mergeDefaults } from "@utils/misc";
import { findByPropsLazy } from "@webpack";
import { SnowflakeUtils } from "@webpack/common";
import { Message } from "discord-types/general";
import type { PartialDeep } from "type-fest";
import { lazyWebpack, mergeDefaults } from "../../utils/misc";
import { filters, waitFor } from "../../webpack";
import { Argument } from "./types";
const createBotMessage = lazyWebpack(filters.byCode('username:"Clyde"'));
const MessageSender = lazyWebpack(filters.byProps(["receiveMessage"]));
let SnowflakeUtils: any;
waitFor("fromTimestamp", m => SnowflakeUtils = m);
const MessageCreator = findByPropsLazy("createBotMessage");
const MessageSender = findByPropsLazy("receiveMessage");
export function generateId() {
return `-${SnowflakeUtils.fromTimestamp(Date.now())}`;
@ -40,7 +38,7 @@ export function generateId() {
* @returns {Message}
*/
export function sendBotMessage(channelId: string, message: PartialDeep<Message>): Message {
const botMessage = createBotMessage({ channelId, content: "", embeds: [] });
const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] });
MessageSender.receiveMessage(channelId, mergeDefaults(message, botMessage));

@ -16,9 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { makeCodeblock } from "../../utils/misc";
import { generateId, sendBotMessage } from "./commandHelpers";
import { ApplicationCommandInputType, ApplicationCommandType, Argument, Command, CommandContext, Option } from "./types";
import { makeCodeblock } from "@utils/text";
import { sendBotMessage } from "./commandHelpers";
import { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType, Argument, Command, CommandContext, Option } from "./types";
export * from "./commandHelpers";
export * from "./types";
@ -79,7 +80,12 @@ export const _handleCommand = function (cmd: Command, args: Argument[], ctx: Com
}
} as never;
function modifyOpt(opt: Option | Command) {
/**
* Prepare a Command Option for Discord by filling missing fields
* @param opt
*/
export function prepareOption<O extends Option | Command>(opt: O): O {
opt.displayName ||= opt.name;
opt.displayDescription ||= opt.description;
opt.options?.forEach((opt, i, opts) => {
@ -88,11 +94,37 @@ function modifyOpt(opt: Option | Command) {
else if (opt === ReqPlaceholder) opts[i] = RequiredMessageOption;
opt.choices?.forEach(x => x.displayName ||= x.name);
modifyOpt(opts[i]);
prepareOption(opts[i]);
});
return opt;
}
// Yes, Discord registers individual commands for each subcommand
// TODO: This probably doesn't support nested subcommands. If that is ever needed,
// investigate
function registerSubCommands(cmd: Command, plugin: string) {
cmd.options?.forEach(o => {
if (o.type !== ApplicationCommandOptionType.SUB_COMMAND)
throw new Error("When specifying sub-command options, all options must be sub-commands.");
const subCmd = {
...cmd,
...o,
type: ApplicationCommandType.CHAT_INPUT,
name: `${cmd.name} ${o.name}`,
id: `${o.name}-${cmd.id}`,
displayName: `${cmd.name} ${o.name}`,
subCommandPath: [{
name: o.name,
type: o.type,
displayName: o.name
}],
rootCommand: cmd
};
registerCommand(subCmd as any, plugin);
});
}
export function registerCommand(command: Command, plugin: string) {
export function registerCommand<C extends Command>(command: C, plugin: string) {
if (!BUILT_IN) {
console.warn(
"[CommandsAPI]",
@ -106,13 +138,19 @@ export function registerCommand(command: Command, plugin: string) {
throw new Error(`Command '${command.name}' already exists.`);
command.isVencordCommand = true;
command.id ??= generateId();
command.id ??= `-${BUILT_IN.length + 1}`;
command.applicationId ??= "-1"; // BUILT_IN;
command.type ??= ApplicationCommandType.CHAT_INPUT;
command.inputType ??= ApplicationCommandInputType.BUILT_IN_TEXT;
command.plugin ||= plugin;
modifyOpt(command);
prepareOption(command);
if (command.options?.[0]?.type === ApplicationCommandOptionType.SUB_COMMAND) {
registerSubCommands(command, plugin);
return;
}
commands[command.name] = command;
BUILT_IN.push(command);
}

@ -24,7 +24,7 @@ export interface CommandContext {
guild?: Guild;
}
export enum ApplicationCommandOptionType {
export const enum ApplicationCommandOptionType {
SUB_COMMAND = 1,
SUB_COMMAND_GROUP = 2,
STRING = 3,
@ -38,7 +38,7 @@ export enum ApplicationCommandOptionType {
ATTACHMENT = 11,
}
export enum ApplicationCommandInputType {
export const enum ApplicationCommandInputType {
BUILT_IN = 0,
BUILT_IN_TEXT = 1,
BUILT_IN_INTEGRATION = 2,
@ -64,7 +64,7 @@ export interface ChoicesOption {
displayName?: string;
}
export enum ApplicationCommandType {
export const enum ApplicationCommandType {
CHAT_INPUT = 1,
USER = 2,
MESSAGE = 3,
@ -81,6 +81,7 @@ export interface Argument {
name: string;
value: string;
focused: undefined;
options: Argument[];
}
export interface Command {

158
src/api/ContextMenu.ts Normal file

@ -0,0 +1,158 @@
/*
* 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 { Logger } from "@utils/Logger";
import type { ReactElement } from "react";
type ContextMenuPatchCallbackReturn = (() => void) | void;
/**
* @param children The rendered context menu elements
* @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example
* @returns A callback which is only ran once used to modify the context menu elements (Use to avoid duplicates)
*/
export type NavContextMenuPatchCallback = (children: Array<ReactElement | null>, ...args: Array<any>) => ContextMenuPatchCallbackReturn;
/**
* @param navId The navId of the context menu being patched
* @param children The rendered context menu elements
* @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example
* @returns A callback which is only ran once used to modify the context menu elements (Use to avoid duplicates)
*/
export type GlobalContextMenuPatchCallback = (navId: string, children: Array<ReactElement | null>, ...args: Array<any>) => ContextMenuPatchCallbackReturn;
const ContextMenuLogger = new Logger("ContextMenu");
export const navPatches = new Map<string, Set<NavContextMenuPatchCallback>>();
export const globalPatches = new Set<GlobalContextMenuPatchCallback>();
/**
* Add a context menu patch
* @param navId The navId(s) for the context menu(s) to patch
* @param patch The patch to be applied
*/
export function addContextMenuPatch(navId: string | Array<string>, patch: NavContextMenuPatchCallback) {
if (!Array.isArray(navId)) navId = [navId];
for (const id of navId) {
let contextMenuPatches = navPatches.get(id);
if (!contextMenuPatches) {
contextMenuPatches = new Set();
navPatches.set(id, contextMenuPatches);
}
contextMenuPatches.add(patch);
}
}
/**
* Add a global context menu patch that fires the patch for all context menus
* @param patch The patch to be applied
*/
export function addGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback) {
globalPatches.add(patch);
}
/**
* Remove a context menu patch
* @param navId The navId(s) for the context menu(s) to remove the patch
* @param patch The patch to be removed
* @returns Whether the patch was successfully removed from the context menu(s)
*/
export function removeContextMenuPatch<T extends string | Array<string>>(navId: T, patch: NavContextMenuPatchCallback): T extends string ? boolean : Array<boolean> {
const navIds = Array.isArray(navId) ? navId : [navId as string];
const results = navIds.map(id => navPatches.get(id)?.delete(patch) ?? false);
return (Array.isArray(navId) ? results : results[0]) as T extends string ? boolean : Array<boolean>;
}
/**
* Remove a global context menu patch
* @param patch The patch to be removed
* @returns Whether the patch was successfully removed
*/
export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback): boolean {
return globalPatches.delete(patch);
}
/**
* 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
*/
export function findGroupChildrenByChildId(id: string | string[], children: Array<ReactElement | null>, _itemsArray?: Array<ReactElement | null>): Array<ReactElement | null> | null {
for (const child of children) {
if (child == null) continue;
if (
(Array.isArray(id) && id.some(id => child.props?.id === id))
|| child.props?.id === id
) return _itemsArray ?? null;
let nextChildren = child.props?.children;
if (nextChildren) {
if (!Array.isArray(nextChildren)) {
nextChildren = [nextChildren];
child.props.children = nextChildren;
}
const found = findGroupChildrenByChildId(id, nextChildren, nextChildren);
if (found !== null) return found;
}
}
return null;
}
interface ContextMenuProps {
contextMenuApiArguments?: Array<any>;
navId: string;
children: Array<ReactElement | null>;
"aria-label": string;
onSelect: (() => void) | undefined;
onClose: (callback: (...args: Array<any>) => any) => void;
}
const patchedMenus = new WeakSet();
export function _patchContextMenu(props: ContextMenuProps) {
props.contextMenuApiArguments ??= [];
const contextMenuPatches = navPatches.get(props.navId);
if (!Array.isArray(props.children)) props.children = [props.children];
if (contextMenuPatches) {
for (const patch of contextMenuPatches) {
try {
const callback = patch(props.children, ...props.contextMenuApiArguments);
if (!patchedMenus.has(props)) callback?.();
} catch (err) {
ContextMenuLogger.error(`Patch for ${props.navId} errored,`, err);
}
}
}
for (const patch of globalPatches) {
try {
const callback = patch(props.navId, props.children, ...props.contextMenuApiArguments);
if (!patchedMenus.has(props)) callback?.();
} catch (err) {
ContextMenuLogger.error("Global patch errored,", err);
}
}
patchedMenus.add(props);
}

@ -1,4 +1,4 @@
/* eslint-disable header/header */
/* eslint-disable simple-header/header */
/*!
* idb-keyval v6.2.0

@ -0,0 +1,64 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { Channel, User } from "discord-types/general/index.js";
interface DecoratorProps {
activities: any[];
channel: Channel;
/**
* Only for DM members
*/
channelName?: string;
/**
* Only for server members
*/
currentUser?: User;
guildId?: string;
isMobile: boolean;
isOwner?: boolean;
isTyping: boolean;
selected: boolean;
status: string;
user: User;
[key: string]: any;
}
export type Decorator = (props: DecoratorProps) => JSX.Element | null;
type OnlyIn = "guilds" | "dms";
export const decorators = new Map<string, { decorator: Decorator, onlyIn?: OnlyIn; }>();
export function addDecorator(identifier: string, decorator: Decorator, onlyIn?: OnlyIn) {
decorators.set(identifier, { decorator, onlyIn });
}
export function removeDecorator(identifier: string) {
decorators.delete(identifier);
}
export function __getDecorators(props: DecoratorProps): (JSX.Element | null)[] {
const isInGuild = !!(props.guildId);
return Array.from(decorators.values(), decoratorObj => {
const { decorator, onlyIn } = decoratorObj;
// this can most likely be done cleaner
if (!onlyIn || (onlyIn === "guilds" && isInGuild) || (onlyIn === "dms" && !isInGuild)) {
return decorator(props);
}
return null;
});
}

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export type AccessoryCallback = (props: Record<string, any>) => JSX.Element;
export type AccessoryCallback = (props: Record<string, any>) => JSX.Element | null | Array<JSX.Element | null>;
export type Accessory = {
callback: AccessoryCallback;
position?: number;
@ -44,6 +44,15 @@ export function _modifyAccessories(
props: Record<string, any>
) {
for (const accessory of accessories.values()) {
let accessories = accessory.callback(props);
if (accessories == null)
continue;
if (!Array.isArray(accessories))
accessories = [accessories];
else if (accessories.length === 0)
continue;
elements.splice(
accessory.position != null
? accessory.position < 0
@ -51,7 +60,7 @@ export function _modifyAccessories(
: accessory.position
: elements.length,
0,
accessory.callback(props)
...accessories.filter(e => e != null) as JSX.Element[]
);
}

@ -0,0 +1,63 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { Channel, Message } from "discord-types/general/index.js";
interface DecorationProps {
author: {
/**
* Will be username if the user has no nickname
*/
nick: string;
iconRoleId: string;
guildMemberAvatar: string;
colorRoleName: string;
colorString: string;
};
channel: Channel;
compact: boolean;
decorations: {
/**
* Element for the [BOT] tag if there is one
*/
0: JSX.Element | null;
/**
* Other decorations (including ones added with this api)
*/
1: JSX.Element[];
};
message: Message;
[key: string]: any;
}
export type Decoration = (props: DecorationProps) => JSX.Element | null;
export const decorations = new Map<string, Decoration>();
export function addDecoration(identifier: string, decoration: Decoration) {
decorations.set(identifier, decoration);
}
export function removeDecoration(identifier: string) {
decorations.delete(identifier);
}
export function __addDecorationsToMessage(props: DecorationProps): (JSX.Element | null)[] {
return [...decorations.values()].map(decoration => {
return decoration(props);
});
}

@ -16,46 +16,91 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import type { Channel,Message } from "discord-types/general";
import Logger from "../utils/logger";
import { Logger } from "@utils/Logger";
import { MessageStore } from "@webpack/common";
import { CustomEmoji } from "@webpack/types";
import type { Channel, Message } from "discord-types/general";
import type { Promisable } from "type-fest";
const MessageEventsLogger = new Logger("MessageEvents", "#e5c890");
export interface Emoji {
require_colons: boolean,
originalName: string,
animated: boolean;
guildId: string,
name: string,
url: string,
id: string,
}
export interface MessageObject {
content: string,
validNonShortcutEmojis: Emoji[];
validNonShortcutEmojis: CustomEmoji[];
invalidEmojis: any[];
tts: boolean;
}
export type SendListener = (channelId: string, messageObj: MessageObject, extra: any) => void;
export type EditListener = (channelId: string, messageId: string, messageObj: MessageObject) => void;
export interface Upload {
classification: string;
currentSize: number;
description: string | null;
filename: string;
id: string;
isImage: boolean;
isVideo: boolean;
item: {
file: File;
platform: number;
};
loaded: number;
mimeType: string;
preCompressionSize: number;
responseUrl: string;
sensitive: boolean;
showLargeMessageDialog: boolean;
spoiler: boolean;
status: "NOT_STARTED" | "STARTED" | "UPLOADING" | "ERROR" | "COMPLETED" | "CANCELLED";
uniqueId: string;
uploadedFilename: string;
}
export interface MessageReplyOptions {
messageReference: Message["messageReference"];
allowedMentions?: {
parse: Array<string>;
repliedUser: boolean;
};
}
export interface MessageExtra {
stickers?: string[];
uploads?: Upload[];
replyOptions: MessageReplyOptions;
content: string;
channel: Channel;
type?: any;
openWarningPopout: (props: any) => any;
}
export type SendListener = (channelId: string, messageObj: MessageObject, extra: MessageExtra) => Promisable<void | { cancel: boolean; }>;
export type EditListener = (channelId: string, messageId: string, messageObj: MessageObject) => Promisable<void>;
const sendListeners = new Set<SendListener>();
const editListeners = new Set<EditListener>();
export function _handlePreSend(channelId: string, messageObj: MessageObject, extra: any) {
export async function _handlePreSend(channelId: string, messageObj: MessageObject, extra: MessageExtra, replyOptions: MessageReplyOptions) {
extra.replyOptions = replyOptions;
for (const listener of sendListeners) {
try {
listener(channelId, messageObj, extra);
} catch (e) { MessageEventsLogger.error(`MessageSendHandler: Listener encoutered an unknown error. (${e})`); }
const result = await listener(channelId, messageObj, extra);
if (result && result.cancel === true) {
return true;
}
} catch (e) {
MessageEventsLogger.error("MessageSendHandler: Listener encountered an unknown error\n", e);
}
}
return false;
}
export function _handlePreEdit(channeld: string, messageId: string, messageObj: MessageObject) {
export async function _handlePreEdit(channelId: string, messageId: string, messageObj: MessageObject) {
for (const listener of editListeners) {
try {
listener(channeld, messageId, messageObj);
} catch (e) { MessageEventsLogger.error(`MessageEditHandler: Listener encoutered an unknown error. (${e})`); }
await listener(channelId, messageId, messageObj);
} catch (e) {
MessageEventsLogger.error("MessageEditHandler: Listener encountered an unknown error\n", e);
}
}
}
@ -87,10 +132,14 @@ type ClickListener = (message: Message, channel: Channel, event: MouseEvent) =>
const listeners = new Set<ClickListener>();
export function _handleClick(message: Message, channel: Channel, event: MouseEvent) {
// message object may be outdated, so (try to) fetch latest one
message = MessageStore.getMessage(channel.id, message.id) ?? message;
for (const listener of listeners) {
try {
listener(message, channel, event);
} catch (e) { MessageEventsLogger.error(`MessageClickHandler: Listener encoutered an unknown error. (${e})`); }
} catch (e) {
MessageEventsLogger.error("MessageClickHandler: Listener encountered an unknown error\n", e);
}
}
}

69
src/api/MessagePopover.ts Normal file

@ -0,0 +1,69 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { Logger } from "@utils/Logger";
import { Channel, Message } from "discord-types/general";
import type { MouseEventHandler } from "react";
const logger = new Logger("MessagePopover");
export interface ButtonItem {
key?: string,
label: string,
icon: React.ComponentType<any>,
message: Message,
channel: Channel,
onClick?: MouseEventHandler<HTMLButtonElement>,
onContextMenu?: MouseEventHandler<HTMLButtonElement>;
}
export type getButtonItem = (message: Message) => ButtonItem | null;
export const buttons = new Map<string, getButtonItem>();
export function addButton(
identifier: string,
item: getButtonItem,
) {
buttons.set(identifier, item);
}
export function removeButton(identifier: string) {
buttons.delete(identifier);
}
export function _buildPopoverElements(
msg: Message,
makeButton: (item: ButtonItem) => React.ComponentType
) {
const items = [] as React.ComponentType[];
for (const [identifier, getItem] of buttons.entries()) {
try {
const item = getItem(msg);
if (item) {
item.key ??= identifier;
items.push(makeButton(item));
}
} catch (err) {
logger.error(`[${identifier}]`, err);
}
}
return items;
}

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { waitFor } from "../webpack";
import { waitFor } from "@webpack";
let NoticesModule: any;
waitFor(m => m.show && m.dismiss && !m.suppressAll, m => NoticesModule = m);

@ -0,0 +1,123 @@
/*
* 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 "./styles.css";
import { useSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { classes } from "@utils/misc";
import { React, useEffect, useMemo, useState, useStateFromStores, WindowStore } from "@webpack/common";
import { NotificationData } from "./Notifications";
export default ErrorBoundary.wrap(function NotificationComponent({
title,
body,
richBody,
color,
icon,
onClick,
onClose,
image,
permanent,
className,
dismissOnClick
}: NotificationData & { className?: string; }) {
const { timeout, position } = useSettings(["notifications.timeout", "notifications.position"]).notifications;
const hasFocus = useStateFromStores([WindowStore], () => WindowStore.isFocused());
const [isHover, setIsHover] = useState(false);
const [elapsed, setElapsed] = useState(0);
const start = useMemo(() => Date.now(), [timeout, isHover, hasFocus]);
useEffect(() => {
if (isHover || !hasFocus || timeout === 0 || permanent) return void setElapsed(0);
const intervalId = setInterval(() => {
const elapsed = Date.now() - start;
if (elapsed >= timeout)
onClose!();
else
setElapsed(elapsed);
}, 10);
return () => clearInterval(intervalId);
}, [timeout, isHover, hasFocus]);
const timeoutProgress = elapsed / timeout;
return (
<button
className={classes("vc-notification-root", className)}
style={position === "bottom-right" ? { bottom: "1rem" } : { top: "3rem" }}
onClick={() => {
onClick?.();
if (dismissOnClick !== false)
onClose!();
}}
onContextMenu={e => {
e.preventDefault();
e.stopPropagation();
onClose!();
}}
onMouseEnter={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
>
<div className="vc-notification">
{icon && <img className="vc-notification-icon" src={icon} alt="" />}
<div className="vc-notification-content">
<div className="vc-notification-header">
<h2 className="vc-notification-title">{title}</h2>
<button
className="vc-notification-close-btn"
onClick={e => {
e.preventDefault();
e.stopPropagation();
onClose!();
}}
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
role="img"
aria-labelledby="vc-notification-dismiss-title"
>
<title id="vc-notification-dismiss-title">Dismiss Notification</title>
<path fill="currentColor" d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z" />
</svg>
</button>
</div>
<div>
{richBody ?? <p className="vc-notification-p">{body}</p>}
</div>
</div>
</div>
{image && <img className="vc-notification-img" src={image} alt="" />}
{timeout !== 0 && !permanent && (
<div
className="vc-notification-progressbar"
style={{ width: `${(1 - timeoutProgress) * 100}%`, backgroundColor: color || "var(--brand-experiment)" }}
/>
)}
</button>
);
}, {
onError: ({ props }) => props.onClose!()
});

@ -0,0 +1,110 @@
/*
* 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 { Settings } from "@api/Settings";
import { Queue } from "@utils/Queue";
import { ReactDOM } from "@webpack/common";
import type { ReactNode } from "react";
import type { Root } from "react-dom/client";
import NotificationComponent from "./NotificationComponent";
import { persistNotification } from "./notificationLog";
const NotificationQueue = new Queue();
let reactRoot: Root;
let id = 42;
function getRoot() {
if (!reactRoot) {
const container = document.createElement("div");
container.id = "vc-notification-container";
document.body.append(container);
reactRoot = ReactDOM.createRoot(container);
}
return reactRoot;
}
export interface NotificationData {
title: string;
body: string;
/**
* Same as body but can be a custom component.
* Will be used over body if present.
* Not supported on desktop notifications, those will fall back to body */
richBody?: ReactNode;
/** Small icon. This is for things like profile pictures and should be square */
icon?: string;
/** Large image. Optimally, this should be around 16x9 but it doesn't matter much. Desktop Notifications might not support this */
image?: string;
onClick?(): void;
onClose?(): void;
color?: string;
/** Whether this notification should not have a timeout */
permanent?: boolean;
/** Whether this notification should not be persisted in the Notification Log */
noPersist?: boolean;
/** Whether this notification should be dismissed when clicked (defaults to true) */
dismissOnClick?: boolean;
}
function _showNotification(notification: NotificationData, id: number) {
const root = getRoot();
return new Promise<void>(resolve => {
root.render(
<NotificationComponent key={id} {...notification} onClose={() => {
notification.onClose?.();
root.render(null);
resolve();
}} />,
);
});
}
function shouldBeNative() {
if (typeof Notification === "undefined") return false;
const { useNative } = Settings.notifications;
if (useNative === "always") return true;
if (useNative === "not-focused") return !document.hasFocus();
return false;
}
export async function requestPermission() {
return (
Notification.permission === "granted" ||
(Notification.permission !== "denied" && (await Notification.requestPermission()) === "granted")
);
}
export async function showNotification(data: NotificationData) {
persistNotification(data);
if (shouldBeNative() && await requestPermission()) {
const { title, body, icon, image, onClick = null, onClose = null } = data;
const n = new Notification(title, {
body,
icon,
image
});
n.onclick = onClick;
n.onclose = onClose;
} else {
NotificationQueue.push(() => _showNotification(data, id++));
}
}

@ -0,0 +1,19 @@
/*
* 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/>.
*/
export * from "./Notifications";

@ -0,0 +1,203 @@
/*
* 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 * as DataStore from "@api/DataStore";
import { Settings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { useAwaiter } from "@utils/react";
import { Alerts, Button, Forms, moment, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
import { nanoid } from "nanoid";
import type { DispatchWithoutAction } from "react";
import NotificationComponent from "./NotificationComponent";
import type { NotificationData } from "./Notifications";
interface PersistentNotificationData extends Pick<NotificationData, "title" | "body" | "image" | "icon" | "color"> {
timestamp: number;
id: string;
}
const KEY = "notification-log";
const getLog = async () => {
const log = await DataStore.get(KEY) as PersistentNotificationData[] | undefined;
return log ?? [];
};
const cl = classNameFactory("vc-notification-log-");
const signals = new Set<DispatchWithoutAction>();
export async function persistNotification(notification: NotificationData) {
if (notification.noPersist) return;
const limit = Settings.notifications.logLimit;
if (limit === 0) return;
await DataStore.update(KEY, (old: PersistentNotificationData[] | undefined) => {
const log = old ?? [];
// Omit stuff we don't need
const {
onClick, onClose, richBody, permanent, noPersist, dismissOnClick,
...pureNotification
} = notification;
log.unshift({
...pureNotification,
timestamp: Date.now(),
id: nanoid()
});
if (log.length > limit && limit !== 200)
log.length = limit;
return log;
});
signals.forEach(x => x());
}
export async function deleteNotification(timestamp: number) {
const log = await getLog();
const index = log.findIndex(x => x.timestamp === timestamp);
if (index === -1) return;
log.splice(index, 1);
await DataStore.set(KEY, log);
signals.forEach(x => x());
}
export function useLogs() {
const [signal, setSignal] = useReducer(x => x + 1, 0);
useEffect(() => {
signals.add(setSignal);
return () => void signals.delete(setSignal);
}, []);
const [log, _, pending] = useAwaiter(getLog, {
fallbackValue: [],
deps: [signal]
});
return [log, pending] as const;
}
function NotificationEntry({ data }: { data: PersistentNotificationData; }) {
const [removing, setRemoving] = useState(false);
const ref = React.useRef<HTMLDivElement>(null);
useEffect(() => {
const div = ref.current!;
const setHeight = () => {
if (div.clientHeight === 0) return requestAnimationFrame(setHeight);
div.style.height = `${div.clientHeight}px`;
};
setHeight();
}, []);
return (
<div className={cl("wrapper", { removing })} ref={ref}>
<NotificationComponent
{...data}
permanent={true}
dismissOnClick={false}
onClose={() => {
if (removing) return;
setRemoving(true);
setTimeout(() => deleteNotification(data.timestamp), 200);
}}
richBody={
<div className={cl("body")}>
{data.body}
<Timestamp timestamp={moment(data.timestamp)} className={cl("timestamp")} />
</div>
}
/>
</div>
);
}
export function NotificationLog({ log, pending }: { log: PersistentNotificationData[], pending: boolean; }) {
if (!log.length && !pending)
return (
<div className={cl("container")}>
<div className={cl("empty")} />
<Forms.FormText style={{ textAlign: "center" }}>
No notifications yet
</Forms.FormText>
</div>
);
return (
<div className={cl("container")}>
{log.map(n => <NotificationEntry data={n} key={n.id} />)}
</div>
);
}
function LogModal({ modalProps, close }: { modalProps: ModalProps; close(): void; }) {
const [log, pending] = useLogs();
return (
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
<ModalHeader>
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>Notification Log</Text>
<ModalCloseButton onClick={close} />
</ModalHeader>
<ModalContent>
<NotificationLog log={log} pending={pending} />
</ModalContent>
<ModalFooter>
<Button
disabled={log.length === 0}
onClick={() => {
Alerts.show({
title: "Are you sure?",
body: `This will permanently remove ${log.length} notification${log.length === 1 ? "" : "s"}. This action cannot be undone.`,
async onConfirm() {
await DataStore.set(KEY, []);
signals.forEach(x => x());
},
confirmText: "Do it!",
confirmColor: "vc-notification-log-danger-btn",
cancelText: "Nevermind"
});
}}
>
Clear Notification Log
</Button>
</ModalFooter>
</ModalRoot>
);
}
export function openNotificationLogModal() {
const key = openModal(modalProps => (
<LogModal
modalProps={modalProps}
close={() => closeModal(key)}
/>
));
}

@ -0,0 +1,122 @@
.vc-notification-root {
/* clear default button styles */
all: unset;
display: flex;
flex-direction: column;
color: var(--text-normal);
background-color: var(--background-secondary-alt);
border-radius: 6px;
overflow: hidden;
cursor: pointer;
width: 100%;
}
.vc-notification-root:not(.vc-notification-log-wrapper > .vc-notification-root) {
position: absolute;
z-index: 2147483647;
right: 1rem;
width: 25vw;
min-height: 10vh;
}
.vc-notification {
display: flex;
flex-direction: row;
padding: 1.25rem;
gap: 1.25rem;
}
.vc-notification-content {
width: 100%;
}
.vc-notification-header {
display: flex;
justify-content: space-between;
}
.vc-notification-title {
color: var(--header-primary);
font-size: 1rem;
font-weight: 600;
line-height: 1.25rem;
text-transform: uppercase;
}
.vc-notification-close-btn {
all: unset;
cursor: pointer;
color: var(--interactive-normal);
opacity: 0.5;
transition: opacity 0.2s ease-in-out, color 0.2s ease-in-out;
}
.vc-notification-close-btn:hover {
color: var(--interactive-hover);
opacity: 1;
}
.vc-notification-icon {
height: 4rem;
width: 4rem;
border-radius: 6px;
}
.vc-notification-progressbar {
height: 0.25rem;
border-radius: 5px;
margin-top: auto;
}
.vc-notification-p {
margin: 0.5rem 0 0;
line-height: 140%;
}
.vc-notification-img {
width: 100%;
}
.vc-notification-log-empty {
height: 218px;
background: url("/assets/b36de980b174d7b798c89f35c116e5c6.svg") center no-repeat;
margin-bottom: 40px;
}
.vc-notification-log-container {
display: flex;
flex-direction: column;
padding: 1em;
overflow: hidden;
}
.vc-notification-log-wrapper {
transition: 200ms ease;
transition-property: height, opacity;
}
.vc-notification-log-wrapper:not(:last-child) {
margin-bottom: 1em;
}
.vc-notification-log-removing {
height: 0 !important;
opacity: 0;
margin-bottom: 1em;
}
.vc-notification-log-body {
display: flex;
flex-direction: column;
}
.vc-notification-log-timestamp {
margin-left: auto;
font-size: 0.8em;
font-weight: lighter;
}
.vc-notification-log-danger-btn {
color: var(--white-500);
background-color: var(--button-danger-background);
}

55
src/api/ServerList.ts Normal file

@ -0,0 +1,55 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { Logger } from "@utils/Logger";
const logger = new Logger("ServerListAPI");
export const enum ServerListRenderPosition {
Above,
In,
}
const renderFunctionsAbove = new Set<Function>();
const renderFunctionsIn = new Set<Function>();
function getRenderFunctions(position: ServerListRenderPosition) {
return position === ServerListRenderPosition.Above ? renderFunctionsAbove : renderFunctionsIn;
}
export function addServerListElement(position: ServerListRenderPosition, renderFunction: Function) {
getRenderFunctions(position).add(renderFunction);
}
export function removeServerListElement(position: ServerListRenderPosition, renderFunction: Function) {
getRenderFunctions(position).delete(renderFunction);
}
export const renderAll = (position: ServerListRenderPosition) => {
const ret: Array<JSX.Element> = [];
for (const renderFunction of getRenderFunctions(position)) {
try {
ret.unshift(renderFunction());
} catch (e) {
logger.error("Failed to render server list element:", e);
}
}
return ret;
};

@ -16,57 +16,118 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { debounce } from "@utils/debounce";
import { localStorage } from "@utils/localStorage";
import { Logger } from "@utils/Logger";
import { mergeDefaults } from "@utils/misc";
import { putCloudSettings } from "@utils/settingsSync";
import { DefinedSettings, OptionType, SettingsChecks, SettingsDefinition } from "@utils/types";
import { React } from "@webpack/common";
import plugins from "~plugins";
import IpcEvents from "../utils/IpcEvents";
import { mergeDefaults } from "../utils/misc";
import { OptionType } from "../utils/types";
import { React } from "../webpack/common";
const logger = new Logger("Settings");
export interface Settings {
notifyAboutUpdates: boolean;
autoUpdate: boolean;
autoUpdateNotification: boolean,
useQuickCss: boolean;
enableReactDevtools: boolean;
themeLinks: string[];
enabledThemes: string[];
frameless: boolean;
transparent: boolean;
winCtrlQ: boolean;
macosTranslucency: boolean;
disableMinSize: boolean;
winNativeTitleBar: boolean;
plugins: {
[plugin: string]: {
enabled: boolean;
[setting: string]: any;
};
};
notifications: {
timeout: number;
position: "top-right" | "bottom-right";
useNative: "always" | "never" | "not-focused";
logLimit: number;
};
cloud: {
authenticated: boolean;
url: string;
settingsSync: boolean;
settingsSyncVersion: number;
};
}
const DefaultSettings: Settings = {
notifyAboutUpdates: true,
autoUpdate: false,
autoUpdateNotification: true,
useQuickCss: true,
themeLinks: [],
enabledThemes: [],
enableReactDevtools: false,
plugins: {}
frameless: false,
transparent: false,
winCtrlQ: false,
macosTranslucency: false,
disableMinSize: false,
winNativeTitleBar: false,
plugins: {},
notifications: {
timeout: 5000,
position: "bottom-right",
useNative: "not-focused",
logLimit: 50
},
cloud: {
authenticated: false,
url: "https://api.vencord.dev/",
settingsSync: false,
settingsSyncVersion: 0
}
};
for (const plugin in plugins) {
DefaultSettings.plugins[plugin] = {
enabled: plugins[plugin].required ?? false
};
}
try {
var settings = JSON.parse(VencordNative.ipc.sendSync(IpcEvents.GET_SETTINGS)) as Settings;
var settings = JSON.parse(VencordNative.settings.get()) as Settings;
mergeDefaults(settings, DefaultSettings);
} catch (err) {
console.error("Corrupt settings file. ", err);
var settings = mergeDefaults({} as Settings, DefaultSettings);
logger.error("An error occurred while loading the settings. Corrupt settings file?\n", err);
}
type SubscriptionCallback = ((newValue: any, path: string) => void) & { _path?: string; };
const saveSettingsOnFrequentAction = debounce(async () => {
if (Settings.cloud.settingsSync && Settings.cloud.authenticated) {
await putCloudSettings();
delete localStorage.Vencord_settingsDirty;
}
}, 60_000);
type SubscriptionCallback = ((newValue: any, path: string) => void) & { _paths?: Array<string>; };
const subscriptions = new Set<SubscriptionCallback>();
const proxyCache = {} as Record<string, any>;
// Wraps the passed settings object in a Proxy to nicely handle change listeners and default values
function makeProxy(settings: Settings, root = settings, path = ""): Settings {
return new Proxy(settings, {
function makeProxy(settings: any, root = settings, path = ""): Settings {
return proxyCache[path] ??= new Proxy(settings, {
get(target, p: string) {
const v = target[p];
// using "in" is important in the following cases to properly handle falsy or nullish values
if (!(p in target)) {
// Return empty for plugins with no settings
if (path === "plugins" && p in plugins)
return target[p] = makeProxy({
enabled: plugins[p].required ?? plugins[p].enabledByDefault ?? false
}, root, `plugins.${p}`);
// Since the property is not set, check if this is a plugin's setting and if so, try to resolve
// the default value.
if (path.startsWith("plugins.")) {
@ -76,9 +137,13 @@ function makeProxy(settings: Settings, root = settings, path = ""): Settings {
if (!setting) return v;
if ("default" in setting)
// normal setting with a default value
return setting.default;
if (setting.type === OptionType.SELECT)
return setting.options.find(o => o.default)?.value;
return (target[p] = setting.default);
if (setting.type === OptionType.SELECT) {
const def = setting.options.find(o => o.default);
if (def)
target[p] = def.value;
return def?.value;
}
}
}
return v;
@ -99,13 +164,17 @@ function makeProxy(settings: Settings, root = settings, path = ""): Settings {
target[p] = v;
// Call any listeners that are listening to a setting of this path
const setPath = `${path}${path && "."}${p}`;
delete proxyCache[setPath];
for (const subscription of subscriptions) {
if (!subscription._path || subscription._path === setPath) {
if (!subscription._paths || subscription._paths.includes(setPath)) {
subscription(v, setPath);
}
}
// And don't forget to persist the settings!
VencordNative.ipc.invoke(IpcEvents.SET_SETTINGS, JSON.stringify(root, null, 4));
PlainSettings.cloud.settingsSyncVersion = Date.now();
localStorage.Vencord_settingsDirty = true;
saveSettingsOnFrequentAction();
VencordNative.settings.set(JSON.stringify(root, null, 4));
return true;
}
});
@ -131,14 +200,20 @@ export const Settings = makeProxy(settings);
* Settings hook for React components. Returns a smart settings
* object that automagically triggers a rerender if any properties
* are altered
* @param paths An optional list of paths to whitelist for rerenders
* @returns Settings
*/
export function useSettings() {
// TODO: Representing paths as essentially "string[].join('.')" wont allow dots in paths, change to "paths?: string[][]" later
export function useSettings(paths?: UseSettings<Settings>[]) {
const [, forceUpdate] = React.useReducer(() => ({}), {});
const onUpdate: SubscriptionCallback = paths
? (value, path) => paths.includes(path as UseSettings<Settings>) && forceUpdate()
: forceUpdate;
React.useEffect(() => {
subscriptions.add(forceUpdate);
return () => void subscriptions.delete(forceUpdate);
subscriptions.add(onUpdate);
return () => void subscriptions.delete(onUpdate);
}, []);
return Settings;
@ -162,6 +237,59 @@ type ResolvePropDeep<T, P> = P extends "" ? T :
export function addSettingsListener<Path extends keyof Settings>(path: Path, onUpdate: (newValue: Settings[Path], path: Path) => void): void;
export function addSettingsListener<Path extends string>(path: Path, onUpdate: (newValue: Path extends "" ? any : ResolvePropDeep<Settings, Path>, path: Path extends "" ? string : Path) => void): void;
export function addSettingsListener(path: string, onUpdate: (newValue: any, path: string) => void) {
(onUpdate as SubscriptionCallback)._path = path;
if (path)
((onUpdate as SubscriptionCallback)._paths ??= []).push(path);
subscriptions.add(onUpdate);
}
export function migratePluginSettings(name: string, ...oldNames: string[]) {
const { plugins } = settings;
if (name in plugins) return;
for (const oldName of oldNames) {
if (oldName in plugins) {
logger.info(`Migrating settings from old name ${oldName} to ${name}`);
plugins[name] = plugins[oldName];
delete plugins[oldName];
VencordNative.settings.set(JSON.stringify(settings, null, 4));
break;
}
}
}
export function definePluginSettings<
Def extends SettingsDefinition,
Checks extends SettingsChecks<Def>,
PrivateSettings extends object = {}
>(def: Def, checks?: Checks) {
const definedSettings: DefinedSettings<Def, Checks, PrivateSettings> = {
get store() {
if (!definedSettings.pluginName) throw new Error("Cannot access settings before plugin is initialized");
return Settings.plugins[definedSettings.pluginName] as any;
},
use: settings => useSettings(
settings?.map(name => `plugins.${definedSettings.pluginName}.${name}`) as UseSettings<Settings>[]
).plugins[definedSettings.pluginName] as any,
def,
checks: checks ?? {} as any,
pluginName: "",
withPrivateSettings<T extends object>() {
return this as DefinedSettings<Def, Checks, T>;
}
};
return definedSettings;
}
type UseSettings<T extends object> = ResolveUseSettings<T>[keyof T];
type ResolveUseSettings<T extends object> = {
[Key in keyof T]:
Key extends string
? T[Key] extends Record<string, unknown>
// @ts-ignore "Type instantiation is excessively deep and possibly infinite"
? UseSettings<T[Key]> extends string ? `${Key}.${UseSettings<T[Key]>}` : never
: Key
: never;
};

162
src/api/Styles.ts Normal file

@ -0,0 +1,162 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 type { MapValue } from "type-fest/source/entry";
export type Style = MapValue<typeof VencordStyles>;
export const styleMap = window.VencordStyles ??= new Map();
export function requireStyle(name: string) {
const style = styleMap.get(name);
if (!style) throw new Error(`Style "${name}" does not exist`);
return style;
}
/**
* A style's name can be obtained from importing a stylesheet with `?managed` at the end of the import
* @param name The name of the style
* @returns `false` if the style was already enabled, `true` otherwise
* @example
* import pluginStyle from "./plugin.css?managed";
*
* // Inside some plugin method like "start()" or "[option].onChange()"
* enableStyle(pluginStyle);
*/
export function enableStyle(name: string) {
const style = requireStyle(name);
if (style.dom?.isConnected)
return false;
if (!style.dom) {
style.dom = document.createElement("style");
style.dom.dataset.vencordName = style.name;
}
compileStyle(style);
document.head.appendChild(style.dom);
return true;
}
/**
* @param name The name of the style
* @returns `false` if the style was already disabled, `true` otherwise
* @see {@link enableStyle} for info on getting the name of an imported style
*/
export function disableStyle(name: string) {
const style = requireStyle(name);
if (!style.dom?.isConnected)
return false;
style.dom.remove();
style.dom = null;
return true;
}
/**
* @param name The name of the style
* @returns `true` in most cases, may return `false` in some edge cases
* @see {@link enableStyle} for info on getting the name of an imported style
*/
export const toggleStyle = (name: string) => isStyleEnabled(name) ? disableStyle(name) : enableStyle(name);
/**
* @param name The name of the style
* @returns Whether the style is enabled
* @see {@link enableStyle} for info on getting the name of an imported style
*/
export const isStyleEnabled = (name: string) => requireStyle(name).dom?.isConnected ?? false;
/**
* Sets the variables of a style
* ```ts
* // -- 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", ... }
*
* // Inside some plugin method like "start()"
* setStyleClassNames(pluginStyle, classNames);
* enableStyle(pluginStyle);
* ```
* ```scss
* // -- plugin.css --
* .plugin-root [--thin]::-webkit-scrollbar { ... }
* ```
* ```scss
* // -- final stylesheet --
* .plugin-root .thin-31rlnD.scrollerBase-_bVAAt::-webkit-scrollbar { ... }
* ```
* @param name The name of the style
* @param classNames An object where the keys are the variable names and the values are the variable values
* @param recompile Whether to recompile the style after setting the variables, defaults to `true`
* @see {@link enableStyle} for info on getting the name of an imported style
*/
export const setStyleClassNames = (name: string, classNames: Record<string, string>, recompile = true) => {
const style = requireStyle(name);
style.classNames = classNames;
if (recompile && isStyleEnabled(style.name))
compileStyle(style);
};
/**
* Updates the stylesheet after doing the following to the sourcecode:
* - Interpolate style classnames
* @param style **_Must_ be a style with a DOM element**
* @see {@link setStyleClassNames} for more info on style classnames
*/
export const compileStyle = (style: Style) => {
if (!style.dom) throw new Error("Style has no DOM element");
style.dom.textContent = style.source
.replace(/\[--(\w+)\]/g, (match, name) => {
const className = style.classNames[name];
return className ? classNameToSelector(className) : match;
});
};
/**
* @param name The classname
* @param prefix A prefix to add each class, defaults to `""`
* @return A css selector for the classname
* @example
* classNameToSelector("foo bar") // => ".foo.bar"
*/
export const classNameToSelector = (name: string, prefix = "") => name.split(" ").map(n => `.${prefix}${n}`).join("");
type ClassNameFactoryArg = string | string[] | Record<string, unknown> | false | null | undefined | 0 | "";
/**
* @param prefix The prefix to add to each class, defaults to `""`
* @returns A classname generator function
* @example
* const cl = classNameFactory("plugin-");
*
* cl("base", ["item", "editable"], { selected: null, disabled: true })
* // => "plugin-base plugin-item plugin-editable plugin-disabled"
*/
export const classNameFactory = (prefix: string = "") => (...args: ClassNameFactoryArg[]) => {
const classNames = new Set<string>();
for (const arg of args) {
if (arg && typeof arg === "string") classNames.add(arg);
else if (Array.isArray(arg)) arg.forEach(name => classNames.add(name));
else if (arg && typeof arg === "object") Object.entries(arg).forEach(([name, value]) => value && classNames.add(name));
}
return Array.from(classNames, name => prefix + name).join(" ");
};

@ -16,11 +16,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import * as $Badges from "./Badges";
import * as $Commands from "./Commands";
import * as $ContextMenu from "./ContextMenu";
import * as $DataStore from "./DataStore";
import * as $MemberListDecorators from "./MemberListDecorators";
import * as $MessageAccessories from "./MessageAccessories";
import * as $MessageDecorations from "./MessageDecorations";
import * as $MessageEventsAPI from "./MessageEvents";
import * as $MessagePopover from "./MessagePopover";
import * as $Notices from "./Notices";
import * as $Notifications from "./Notifications";
import * as $ServerList from "./ServerList";
import * as $Settings from "./Settings";
import * as $Styles from "./Styles";
/**
* An API allowing you to listen to Message Clicks or run your own logic
@ -28,16 +37,16 @@ import * as $Notices from "./Notices";
*
* If your plugin uses this, you must add MessageEventsAPI to its dependencies
*/
const MessageEvents = $MessageEventsAPI;
export const MessageEvents = $MessageEventsAPI;
/**
* An API allowing you to create custom notices
* (snackbars on the top, like the Update prompt)
*/
const Notices = $Notices;
export const Notices = $Notices;
/**
* An API allowing you to register custom commands
*/
const Commands = $Commands;
export const Commands = $Commands;
/**
* A wrapper around IndexedDB. This can store arbitrarily
* large data and supports a lot of datatypes (Blob, Map, ...).
@ -52,10 +61,46 @@ const Commands = $Commands;
* This is actually just idb-keyval, so if you're familiar with that, you're golden!
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types}
*/
const DataStore = $DataStore;
export const DataStore = $DataStore;
/**
* An API allowing you to add custom components as message accessories
*/
const MessageAccessories = $MessageAccessories;
export const MessageAccessories = $MessageAccessories;
/**
* An API allowing you to add custom buttons in the message popover
*/
export const MessagePopover = $MessagePopover;
/**
* An API allowing you to add badges to user profiles
*/
export const Badges = $Badges;
/**
* An API allowing you to add custom elements to the server list
*/
export const ServerList = $ServerList;
/**
* An API allowing you to add components as message accessories
*/
export const MessageDecorations = $MessageDecorations;
/**
* An API allowing you to add components to member list users, in both DM's and servers
*/
export const MemberListDecorators = $MemberListDecorators;
/**
* An API allowing you to persist data
*/
export const Settings = $Settings;
/**
* An API allowing you to dynamically load styles
* a
*/
export const Styles = $Styles;
/**
* An API allowing you to display notifications
*/
export const Notifications = $Notifications;
export { Commands,DataStore, MessageAccessories, MessageEvents, Notices };
/**
* An api allowing you to patch and add/remove items to/from context menus
*/
export const ContextMenu = $ContextMenu;

29
src/components/Badge.tsx Normal file

@ -0,0 +1,29 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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/>.
*/
export function Badge({ text, color }): JSX.Element {
return (
<div className="vc-plugins-badge" style={{
backgroundColor: color,
justifySelf: "flex-end",
marginLeft: "auto"
}}>
{text}
</div>
);
}

@ -0,0 +1,68 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { React, TextInput } from "@webpack/common";
// TODO: Refactor settings to use this as well
interface TextInputProps {
/**
* WARNING: Changing this between renders will have no effect!
*/
value: string;
/**
* This will only be called if the new value passed validate()
*/
onChange(newValue: string): void;
/**
* Optionally validate the user input
* Return true if the input is valid
* Otherwise, return a string containing the reason for this input being invalid
*/
validate(v: string): true | string;
}
/**
* A very simple wrapper around Discord's TextInput that validates input and shows
* the user an error message and only calls your onChange when the input is valid
*/
export function CheckedTextInput({ value: initialValue, onChange, validate }: TextInputProps) {
const [value, setValue] = React.useState(initialValue);
const [error, setError] = React.useState<string>();
function handleChange(v: string) {
setValue(v);
const res = validate(v);
if (res === true) {
setError(void 0);
onChange(v);
} else {
setError(res);
}
}
return (
<>
<TextInput
type="text"
value={value}
onChange={handleChange}
error={error}
/>
</>
);
}

@ -0,0 +1,21 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { findByPropsLazy } from "@webpack";
import { Parser } from "@webpack/common";
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer");
/**
* Renders code in a Discord codeblock
*/
export function CodeBlock(props: { content?: string, lang: string; }) {
return (
<div className={CodeContainerClasses.markup}>
{Parser.defaultRules.codeBlock.react(props, null, {})}
</div>
);
}

@ -0,0 +1,35 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { Button } from "@webpack/common";
import { Heart } from "./Heart";
export default function DonateButton(props: any) {
return (
<Button
{...props}
look={Button.Looks.LINK}
color={Button.Colors.TRANSPARENT}
onClick={() => VencordNative.native.openExternal("https://github.com/sponsors/Vendicated")}
>
<Heart />
Donate
</Button>
);
}

@ -16,14 +16,25 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Logger from "../utils/logger";
import { Margins, React } from "../webpack/common";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins";
import { LazyComponent } from "@utils/react";
import { React } from "@webpack/common";
import { ErrorCard } from "./ErrorCard";
interface Props {
interface Props<T = any> {
/** Render nothing if an error occurs */
noop?: boolean;
/** Fallback component to render if an error occurs */
fallback?: React.ComponentType<React.PropsWithChildren<{ error: any; message: string; stack: string; }>>;
onError?(error: Error, errorInfo: React.ErrorInfo): void;
/** called when an error occurs. The props property is only available if using .wrap */
onError?(data: { error: Error, errorInfo: React.ErrorInfo, props: T; }): void;
/** Custom error message */
message?: string;
/** The props passed to the wrapped component. Only used by wrap */
wrappedProps?: T;
}
const color = "#e78284";
@ -32,68 +43,75 @@ const logger = new Logger("React ErrorBoundary", color);
const NO_ERROR = {};
export default class ErrorBoundary extends React.Component<React.PropsWithChildren<Props>> {
static wrap<T = any>(Component: React.ComponentType<T>): (props: T) => React.ReactElement {
return props => (
<ErrorBoundary>
<Component {...props as any/* I hate react typings ??? */} />
</ErrorBoundary>
);
}
// We might want to import this in a place where React isn't ready yet.
// Thus, wrap in a LazyComponent
const ErrorBoundary = LazyComponent(() => {
return class ErrorBoundary extends React.PureComponent<React.PropsWithChildren<Props>> {
state = {
error: NO_ERROR as any,
stack: "",
message: ""
};
state = {
error: NO_ERROR as any,
stack: "",
message: ""
};
static getDerivedStateFromError(error: any) {
let stack = error?.stack ?? "";
let message = error?.message || String(error);
static getDerivedStateFromError(error: any) {
let stack = error?.stack ?? "";
let message = error?.message || String(error);
if (error instanceof Error && stack) {
const eolIdx = stack.indexOf("\n");
if (eolIdx !== -1) {
message = stack.slice(0, eolIdx);
stack = stack.slice(eolIdx + 1).replace(/https:\/\/\S+\/assets\//g, "");
if (error instanceof Error && stack) {
const eolIdx = stack.indexOf("\n");
if (eolIdx !== -1) {
message = stack.slice(0, eolIdx);
stack = stack.slice(eolIdx + 1).replace(/https:\/\/\S+\/assets\//g, "");
}
}
return { error, stack, message };
}
return { error, stack, message };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
this.props.onError?.({ error, errorInfo, props: this.props.wrappedProps });
logger.error("A component threw an Error\n", error);
logger.error("Component Stack", errorInfo.componentStack);
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
this.props.onError?.(error, errorInfo);
logger.error("A component threw an Error\n", error);
logger.error("Component Stack", errorInfo.componentStack);
}
render() {
if (this.state.error === NO_ERROR) return this.props.children;
render() {
if (this.state.error === NO_ERROR) return this.props.children;
if (this.props.noop) return null;
if (this.props.fallback)
return <this.props.fallback
children={this.props.children}
{...this.state}
/>;
if (this.props.fallback)
return <this.props.fallback
children={this.props.children}
{...this.state}
/>;
const msg = this.props.message || "An error occurred while rendering this Component. More info can be found below and in your console.";
const msg = this.props.message || "An error occurred while rendering this Component. More info can be found below and in your console.";
return (
<ErrorCard style={{
overflow: "hidden",
}}>
<h1>Oh no!</h1>
<p>{msg}</p>
<code>
{this.state.message}
{!!this.state.stack && (
<pre className={Margins.marginTop8}>
{this.state.stack}
</pre>
)}
</code>
</ErrorCard>
);
}
}
return (
<ErrorCard style={{ overflow: "hidden" }}>
<h1>Oh no!</h1>
<p>{msg}</p>
<code>
{this.state.message}
{!!this.state.stack && (
<pre className={Margins.top8}>
{this.state.stack}
</pre>
)}
</code>
</ErrorCard>
);
}
};
}) as
React.ComponentType<React.PropsWithChildren<Props>> & {
wrap<T extends object = any>(Component: React.ComponentType<T>, errorBoundaryProps?: Omit<Props<T>, "wrappedProps">): React.FunctionComponent<T>;
};
ErrorBoundary.wrap = (Component, errorBoundaryProps) => props => (
<ErrorBoundary {...errorBoundaryProps} wrappedProps={props}>
<Component {...props} />
</ErrorBoundary>
);
export default ErrorBoundary;

@ -0,0 +1,7 @@
.vc-error-card {
padding: 2em;
background-color: #e7828430;
border: 1px solid #e78284;
border-radius: 5px;
color: var(--text-normal, white);
}

@ -16,24 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Card } from "../webpack/common";
import "./ErrorCard.css";
interface Props {
style?: React.CSSProperties;
className?: string;
}
export function ErrorCard(props: React.PropsWithChildren<Props>) {
import { classes } from "@utils/misc";
import type { HTMLProps } from "react";
export function ErrorCard(props: React.PropsWithChildren<HTMLProps<HTMLDivElement>>) {
return (
<Card className={props.className} style={
{
padding: "2em",
backgroundColor: "#e7828430",
borderColor: "#e78284",
color: "var(--text-normal)",
...props.style
}
}>
<div {...props} className={classes(props.className, "vc-error-card")}>
{props.children}
</Card>
</div>
);
}

@ -0,0 +1,12 @@
.vc-expandableheader-center-flex {
display: flex;
justify-items: center;
align-items: center;
}
.vc-expandableheader-btn {
all: unset;
cursor: pointer;
width: 24px;
height: 24px;
}

@ -0,0 +1,108 @@
/*
* 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 { classNameFactory } from "@api/Styles";
import { Text, Tooltip, useState } from "@webpack/common";
export const cl = classNameFactory("vc-expandableheader-");
import "./ExpandableHeader.css";
export interface ExpandableHeaderProps {
onMoreClick?: () => void;
moreTooltipText?: string;
onDropDownClick?: (state: boolean) => void;
defaultState?: boolean;
headerText: string;
children: React.ReactNode;
buttons?: React.ReactNode[];
}
export default function ExpandableHeader({ children, onMoreClick, buttons, moreTooltipText, defaultState = false, onDropDownClick, headerText }: ExpandableHeaderProps) {
const [showContent, setShowContent] = useState(defaultState);
return (
<>
<div style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "8px"
}}>
<Text
tag="h2"
variant="eyebrow"
style={{
color: "var(--header-primary)",
display: "inline"
}}
>
{headerText}
</Text>
<div className={cl("center-flex")}>
{
buttons ?? null
}
{
onMoreClick && // only show more button if callback is provided
<Tooltip text={moreTooltipText}>
{tooltipProps => (
<button
{...tooltipProps}
className={cl("btn")}
onClick={onMoreClick}>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
>
<path fill="var(--text-normal)" d="M7 12.001C7 10.8964 6.10457 10.001 5 10.001C3.89543 10.001 3 10.8964 3 12.001C3 13.1055 3.89543 14.001 5 14.001C6.10457 14.001 7 13.1055 7 12.001ZM14 12.001C14 10.8964 13.1046 10.001 12 10.001C10.8954 10.001 10 10.8964 10 12.001C10 13.1055 10.8954 14.001 12 14.001C13.1046 14.001 14 13.1055 14 12.001ZM19 10.001C20.1046 10.001 21 10.8964 21 12.001C21 13.1055 20.1046 14.001 19 14.001C17.8954 14.001 17 13.1055 17 12.001C17 10.8964 17.8954 10.001 19 10.001Z" />
</svg>
</button>
)}
</Tooltip>
}
<Tooltip text={showContent ? "Hide " + headerText : "Show " + headerText}>
{tooltipProps => (
<button
{...tooltipProps}
className={cl("btn")}
onClick={() => {
setShowContent(v => !v);
onDropDownClick?.(showContent);
}}
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
transform={showContent ? "scale(1 -1)" : "scale(1 1)"}
>
<path fill="var(--text-normal)" d="M16.59 8.59003L12 13.17L7.41 8.59003L6 10L12 16L18 10L16.59 8.59003Z" />
</svg>
</button>
)}
</Tooltip>
</div>
</div>
{showContent && children}
</>
);
}

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import type { React } from "../webpack/common";
import type { React } from "@webpack/common";
export function Flex(props: React.PropsWithChildren<{
flexDirection?: React.CSSProperties["flexDirection"];
@ -24,9 +24,11 @@ export function Flex(props: React.PropsWithChildren<{
className?: string;
} & React.HTMLProps<HTMLDivElement>>) {
props.style ??= {};
props.style.flexDirection ||= props.flexDirection;
props.style.gap ??= "1em";
props.style.display = "flex";
// TODO(ven): Remove me, what was I thinking??
props.style.gap ??= "1em";
props.style.flexDirection ||= props.flexDirection;
delete props.flexDirection;
return (
<div {...props}>
{props.children}

35
src/components/Heart.tsx Normal file

@ -0,0 +1,35 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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/>.
*/
export function Heart() {
return (
<svg
aria-hidden="true"
height="16"
viewBox="0 0 16 16"
width="16"
style={{ marginRight: "0.5em", transform: "translateY(2px)" }}
>
<path
fill="#db61a2"
fill-rule="evenodd"
d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z"
/>
</svg>
);
}

257
src/components/Icons.tsx Normal file

@ -0,0 +1,257 @@
/*
* 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 "./iconStyles.css";
import { classes } from "@utils/misc";
import { i18n } from "@webpack/common";
import type { PropsWithChildren, SVGProps } from "react";
interface BaseIconProps extends IconProps {
viewBox: string;
}
interface IconProps extends SVGProps<SVGSVGElement> {
className?: string;
height?: string | number;
width?: string | number;
}
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
return (
<svg
className={classes(className, "vc-icon")}
role="img"
width={width}
height={height}
viewBox={viewBox}
{...svgProps}
>
{children}
</svg>
);
}
/**
* Discord's link icon, as seen in the Message context menu "Copy Message Link" option
*/
export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
return (
<Icon
height={height}
width={width}
className={classes(className, "vc-link-icon")}
viewBox="0 0 24 24"
>
<g fill="none" fill-rule="evenodd">
<path fill="currentColor" d="M10.59 13.41c.41.39.41 1.03 0 1.42-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0 5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.982 2.982 0 0 0 0-4.24 2.982 2.982 0 0 0-4.24 0l-3.53 3.53a2.982 2.982 0 0 0 0 4.24zm2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0 5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.982 2.982 0 0 0 0 4.24 2.982 2.982 0 0 0 4.24 0l3.53-3.53a2.982 2.982 0 0 0 0-4.24.973.973 0 0 1 0-1.42z" />
<rect width={width} height={height} />
</g>
</Icon>
);
}
/**
* 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 (
<Icon
{...props}
className={classes(props.className, "vc-copy-icon")}
viewBox="0 0 24 24"
>
<g fill="currentColor">
<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>
);
}
/**
* Discord's open external icon, as seen in the user profile connections
*/
export function OpenExternalIcon(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-open-external-icon")}
viewBox="0 0 24 24"
>
<polygon
fill="currentColor"
fillRule="nonzero"
points="13 20 11 20 11 8 5.5 13.5 4.08 12.08 12 4.16 19.92 12.08 18.5 13.5 13 8"
/>
</Icon>
);
}
export function ImageIcon(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-image-icon")}
viewBox="0 0 24 24"
>
<path fill="currentColor" d="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
</Icon>
);
}
export function InfoIcon(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-info-icon")}
viewBox="0 0 24 24"
>
<path
fill="currentColor"
transform="translate(2 2)"
d="M9,7 L11,7 L11,5 L9,5 L9,7 Z M10,18 C5.59,18 2,14.41 2,10 C2,5.59 5.59,2 10,2 C14.41,2 18,5.59 18,10 C18,14.41 14.41,18 10,18 L10,18 Z M10,4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16,4.4771525 0,10 C-1.33226763e-15,12.6521649 1.0535684,15.195704 2.92893219,17.0710678 C4.80429597,18.9464316 7.3478351,20 10,20 C12.6521649,20 15.195704,18.9464316 17.0710678,17.0710678 C18.9464316,15.195704 20,12.6521649 20,10 C20,7.3478351 18.9464316,4.80429597 17.0710678,2.92893219 C15.195704,1.0535684 12.6521649,2.22044605e-16 10,0 L10,4.4408921e-16 Z M9,15 L11,15 L11,9 L9,9 L9,15 L9,15 Z"
/>
</Icon>
);
}
export function OwnerCrownIcon(props: IconProps) {
return (
<Icon
aria-label={i18n.Messages.GUILD_OWNER}
{...props}
className={classes(props.className, "vc-owner-crown-icon")}
role="img"
viewBox="0 0 16 16"
>
<path
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
d="M13.6572 5.42868C13.8879 5.29002 14.1806 5.30402 14.3973 5.46468C14.6133 5.62602 14.7119 5.90068 14.6473 6.16202L13.3139 11.4954C13.2393 11.7927 12.9726 12.0007 12.6666 12.0007H3.33325C3.02725 12.0007 2.76058 11.792 2.68592 11.4954L1.35258 6.16202C1.28792 5.90068 1.38658 5.62602 1.60258 5.46468C1.81992 5.30468 2.11192 5.29068 2.34325 5.42868L5.13192 7.10202L7.44592 3.63068C7.46173 3.60697 7.48377 3.5913 7.50588 3.57559C7.5192 3.56612 7.53255 3.55663 7.54458 3.54535L6.90258 2.90268C6.77325 2.77335 6.77325 2.56068 6.90258 2.43135L7.76458 1.56935C7.89392 1.44002 8.10658 1.44002 8.23592 1.56935L9.09792 2.43135C9.22725 2.56068 9.22725 2.77335 9.09792 2.90268L8.45592 3.54535C8.46794 3.55686 8.48154 3.56651 8.49516 3.57618C8.51703 3.5917 8.53897 3.60727 8.55458 3.63068L10.8686 7.10202L13.6572 5.42868ZM2.66667 12.6673H13.3333V14.0007H2.66667V12.6673Z"
/>
</Icon>
);
}
/**
* Discord's screenshare icon, as seen in the connection panel
*/
export function ScreenshareIcon(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-screenshare-icon")}
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M2 4.5C2 3.397 2.897 2.5 4 2.5H20C21.103 2.5 22 3.397 22 4.5V15.5C22 16.604 21.103 17.5 20 17.5H13V19.5H17V21.5H7V19.5H11V17.5H4C2.897 17.5 2 16.604 2 15.5V4.5ZM13.2 14.3375V11.6C9.864 11.6 7.668 12.6625 6 15C6.672 11.6625 8.532 8.3375 13.2 7.6625V5L18 9.6625L13.2 14.3375Z"
/>
</Icon>
);
}
export function ImageVisible(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-image-visible")}
viewBox="0 0 24 24"
>
<path fill="currentColor" d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h14q.825 0 1.413.587Q21 4.175 21 5v14q0 .825-.587 1.413Q19.825 21 19 21Zm0-2h14V5H5v14Zm1-2h12l-3.75-5-3 4L9 13Zm-1 2V5v14Z" />
</Icon>
);
}
export function ImageInvisible(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-image-invisible")}
viewBox="0 0 24 24"
>
<path fill="currentColor" d="m21 18.15-2-2V5H7.85l-2-2H19q.825 0 1.413.587Q21 4.175 21 5Zm-1.2 4.45L18.2 21H5q-.825 0-1.413-.587Q3 19.825 3 19V5.8L1.4 4.2l1.4-1.4 18.4 18.4ZM6 17l3-4 2.25 3 .825-1.1L5 7.825V19h11.175l-2-2Zm7.425-6.425ZM10.6 13.4Z" />
</Icon>
);
}
export function Microphone(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-microphone")}
viewBox="0 0 24 24"
>
<path fillRule="evenodd" clipRule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V21H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1ZM12 4C11.2 4 11 4.66667 11 5V11C11 11.3333 11.2 12 12 12C12.8 12 13 11.3333 13 11V5C13 4.66667 12.8 4 12 4Z" fill="currentColor" />
<path fillRule="evenodd" clipRule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V22H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1Z" fill="currentColor" />
</Icon >
);
}
export function CogWheel(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-cog-wheel")}
viewBox="0 0 24 24"
>
<path
clipRule="evenodd"
fill="currentColor"
d="M19.738 10H22V14H19.739C19.498 14.931 19.1 15.798 18.565 16.564L20 18L18 20L16.565 18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069 19.498 8.203 19.099 7.436 18.564L6 20L4 18L5.436 16.564C4.901 15.799 4.502 14.932 4.262 14H2V10H4.262C4.502 9.068 4.9 8.202 5.436 7.436L4 6L6 4L7.436 5.436C8.202 4.9 9.068 4.502 10 4.262V2H14V4.261C14.932 4.502 15.797 4.9 16.565 5.435L18 3.999L20 5.999L18.564 7.436C19.099 8.202 19.498 9.069 19.738 10ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z"
/>
</Icon>
);
}
export function ReplyIcon(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-reply-icon")}
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10 8.26667V4L3 11.4667L10 18.9333V14.56C15 14.56 18.5 16.2667 21 20C20 14.6667 17 9.33333 10 8.26667Z"
/>
</Icon>
);
}
export function DeleteIcon(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-delete-icon")}
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M15 3.999V2H9V3.999H3V5.999H21V3.999H15Z"
/>
<path
fill="currentColor"
d="M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z"
/>
</Icon>
);
}

@ -16,21 +16,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { React } from "../webpack/common";
import { React } from "@webpack/common";
interface Props {
href: string;
interface Props extends React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement> {
disabled?: boolean;
style?: React.CSSProperties;
}
export function Link(props: React.PropsWithChildren<Props>) {
if (props.disabled) {
props.style ??= {};
props.style.pointerEvents = "none";
props["aria-disabled"] = true;
}
return (
<a href={props.href} target="_blank" style={props.style}>
<a role="link" target="_blank" {...props}>
{props.children}
</a>
);

@ -1,42 +0,0 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 monacoHtml from "~fileContent/monacoWin.html";
import { IpcEvents } from "../utils";
import { debounce } from "../utils/debounce";
import { Queue } from "../utils/Queue";
import { find } from "../webpack/webpack";
const queue = new Queue();
const setCss = debounce((css: string) => {
queue.add(() => VencordNative.ipc.invoke(IpcEvents.SET_QUICK_CSS, css));
});
export async function launchMonacoEditor() {
const win = open("about:blank", void 0, "popup,width=1000,height=1000")!;
win.setCss = setCss;
win.getCurrentCss = () => VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS);
win.getTheme = () => find(m => m.ProtoClass?.typeName.endsWith("PreloadedUserSettings"))
.getCurrentValue().appearance.theme === 1
? "vs-dark"
: "vs-light";
win.document.write(monacoHtml);
}

@ -0,0 +1,113 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./contributorModal.css";
import { useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { DevsById } from "@utils/constants";
import { fetchUserProfile, getTheme, Theme } from "@utils/discord";
import { ModalContent, ModalRoot, openModal } from "@utils/modal";
import { Forms, MaskedLink, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
import { User } from "discord-types/general";
import Plugins from "~plugins";
import { PluginCard } from ".";
const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
const cl = classNameFactory("vc-author-modal-");
export function openContributorModal(user: User) {
openModal(modalProps =>
<ModalRoot {...modalProps}>
<ErrorBoundary>
<ModalContent className={cl("root")}>
<ContributorModal user={user} />
</ModalContent>
</ErrorBoundary>
</ModalRoot>
);
}
function GithubIcon() {
const src = getTheme() === Theme.Light ? GithubIconLight : GithubIconDark;
return <img src={src} alt="GitHub" />;
}
function WebsiteIcon() {
const src = getTheme() === Theme.Light ? WebsiteIconLight : WebsiteIconDark;
return <img src={src} alt="Website" />;
}
function ContributorModal({ user }: { user: User; }) {
useSettings();
const profile = useStateFromStores([UserProfileStore], () => UserProfileStore.getUserProfile(user.id));
useEffect(() => {
if (!profile && !user.bot && user.id)
fetchUserProfile(user.id);
}, [user.id]);
const githubName = profile?.connectedAccounts?.find(a => a.type === "github")?.name;
const website = profile?.connectedAccounts?.find(a => a.type === "domain")?.name;
const plugins = useMemo(() => {
const allPlugins = Object.values(Plugins);
const pluginsByAuthor = DevsById[user.id]
? allPlugins.filter(p => p.authors.includes(DevsById[user.id]))
: allPlugins.filter(p => p.authors.some(a => a.name === user.username));
return pluginsByAuthor
.filter(p => !p.name.endsWith("API"))
.sort((a, b) => Number(a.required ?? false) - Number(b.required ?? false));
}, [user.id, user.username]);
return (
<>
<div className={cl("header")}>
<img
className={cl("avatar")}
src={user.getAvatarURL(void 0, 512, true)}
alt=""
/>
<Forms.FormTitle tag="h2" className={cl("name")}>{user.username}</Forms.FormTitle>
<div className={cl("links")}>
{website && (
<MaskedLink
href={"https://" + website}
>
<WebsiteIcon />
</MaskedLink>
)}
{githubName && (
<MaskedLink href={`https://github.com/${githubName}`}>
<GithubIcon />
</MaskedLink>
)}
</div>
</div>
<div className={cl("plugins")}>
{plugins.map(p =>
<PluginCard
key={p.name}
plugin={p}
disabled={p.required ?? false}
onRestartNeeded={() => showToast("Restart to apply changes!")}
/>
)}
</div>
</>
);
}

@ -16,28 +16,34 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { generateId } from "@api/Commands";
import { useSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { proxyLazy } from "@utils/lazy";
import { Margins } from "@utils/margins";
import { classes, isObjectEmpty } from "@utils/misc";
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
import { LazyComponent } from "@utils/react";
import { OptionType, Plugin } from "@utils/types";
import { findByCode, findByPropsLazy } from "@webpack";
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
import { User } from "discord-types/general";
import { Constructor } from "type-fest";
import { generateId } from "../../api/Commands";
import { useSettings } from "../../api/settings";
import { lazyWebpack, proxyLazy } from "../../utils";
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "../../utils/modal";
import { OptionType, Plugin } from "../../utils/types";
import { filters } from "../../webpack";
import { Button, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "../../webpack/common";
import ErrorBoundary from "../ErrorBoundary";
import { Flex } from "../Flex";
import {
ISettingElementProps,
SettingBooleanComponent,
SettingInputComponent,
SettingCustomComponent,
SettingNumericComponent,
SettingSelectComponent,
SettingSliderComponent,
SettingTextComponent
} from "./components";
import { openContributorModal } from "./ContributorModal";
const UserSummaryItem = lazyWebpack(filters.byCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
const AvatarStyles = lazyWebpack(filters.byProps(["moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"]));
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
interface PluginModalProps extends ModalProps {
@ -45,11 +51,12 @@ interface PluginModalProps extends ModalProps {
onRestartNeeded(): void;
}
/** To stop discord making unwanted requests... */
function makeDummyUser(user: { name: string, id: BigInt; }) {
function makeDummyUser(user: { username: string; id?: string; avatar?: string; }) {
const newUser = new UserRecord({
username: user.name,
id: generateId(),
username: user.username,
id: user.id ?? generateId(),
avatar: user.avatar,
/** To stop discord making unwanted requests... */
bot: true,
});
FluxDispatcher.dispatch({
@ -59,6 +66,16 @@ function makeDummyUser(user: { name: string, id: BigInt; }) {
return newUser;
}
const Components: Record<OptionType, React.ComponentType<ISettingElementProps<any>>> = {
[OptionType.STRING]: SettingTextComponent,
[OptionType.NUMBER]: SettingNumericComponent,
[OptionType.BIGINT]: SettingNumericComponent,
[OptionType.BOOLEAN]: SettingBooleanComponent,
[OptionType.SELECT]: SettingSelectComponent,
[OptionType.SLIDER]: SettingSliderComponent,
[OptionType.COMPONENT]: SettingCustomComponent
};
export default function PluginModal({ plugin, onRestartNeeded, onClose, transitionState }: PluginModalProps) {
const [authors, setAuthors] = React.useState<Partial<User>[]>([]);
@ -67,23 +84,39 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
const [tempSettings, setTempSettings] = React.useState<Record<string, any>>({});
const [errors, setErrors] = React.useState<Record<string, boolean>>({});
const [saveError, setSaveError] = React.useState<string | null>(null);
const canSubmit = () => Object.values(errors).every(e => !e);
const hasSettings = Boolean(pluginSettings && plugin.options && !isObjectEmpty(plugin.options));
React.useEffect(() => {
(async () => {
for (const user of plugin.authors.slice(0, 6)) {
const author = user.id ? await UserUtils.fetchUser(`${user.id}`).catch(() => null) : makeDummyUser(user);
setAuthors(a => [...a, author || makeDummyUser(user)]);
const author = user.id
? await UserUtils.getUser(`${user.id}`)
.catch(() => makeDummyUser({ username: user.name }))
: makeDummyUser({ username: user.name });
setAuthors(a => [...a, author]);
}
})();
}, []);
function saveAndClose() {
async function saveAndClose() {
if (!plugin.options) {
onClose();
return;
}
if (plugin.beforeSave) {
const result = await Promise.resolve(plugin.beforeSave(tempSettings));
if (result !== true) {
setSaveError(result);
return;
}
}
let restartNeeded = false;
for (const [key, value] of Object.entries(tempSettings)) {
const option = plugin.options[key];
@ -96,46 +129,36 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
}
function renderSettings() {
if (!pluginSettings || !plugin.options) {
if (!hasSettings || !plugin.options) {
return <Forms.FormText>There are no settings for this plugin.</Forms.FormText>;
} else {
const options = Object.entries(plugin.options).map(([key, setting]) => {
if (setting.hidden) return null;
function onChange(newValue: any) {
setTempSettings(s => ({ ...s, [key]: newValue }));
}
function onError(hasError: boolean) {
setErrors(e => ({ ...e, [key]: hasError }));
}
const Component = Components[setting.type];
return (
<Component
id={key}
key={key}
option={setting}
onChange={onChange}
onError={onError}
pluginSettings={pluginSettings}
definedSettings={plugin.settings}
/>
);
});
return <Flex flexDirection="column" style={{ gap: 12, marginBottom: 16 }}>{options}</Flex>;
}
const options: JSX.Element[] = [];
for (const [key, setting] of Object.entries(plugin.options)) {
function onChange(newValue) {
setTempSettings(s => ({ ...s, [key]: newValue }));
}
function onError(hasError: boolean) {
setErrors(e => ({ ...e, [key]: hasError }));
}
const props = { onChange, pluginSettings, id: key, onError };
switch (setting.type) {
case OptionType.SELECT: {
options.push(<SettingSelectComponent key={key} option={setting} {...props} />);
break;
}
case OptionType.STRING: {
options.push(<SettingInputComponent key={key} option={setting} {...props} />);
break;
}
case OptionType.NUMBER:
case OptionType.BIGINT: {
options.push(<SettingNumericComponent key={key} option={setting} {...props} />);
break;
}
case OptionType.BOOLEAN: {
options.push(<SettingBooleanComponent key={key} option={setting} {...props} />);
break;
}
case OptionType.SLIDER: {
options.push(<SettingSliderComponent key={key} option={setting} {...props} />);
break;
}
}
}
return <Flex flexDirection="column" style={{ gap: 12 }}>{options}</Flex>;
}
function renderMoreUsers(_label: string, count: number) {
@ -159,15 +182,17 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
}
return (
<ModalRoot transitionState={transitionState} size={ModalSize.MEDIUM}>
<ModalHeader>
<Text variant="heading-md/bold">{plugin.name}</Text>
<ModalRoot transitionState={transitionState} size={ModalSize.MEDIUM} className="vc-text-selectable">
<ModalHeader separator={false}>
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>{plugin.name}</Text>
<ModalCloseButton onClick={onClose} />
</ModalHeader>
<ModalContent style={{ marginBottom: 8, marginTop: 8 }}>
<ModalContent>
<Forms.FormSection>
<Forms.FormTitle tag="h3">About {plugin.name}</Forms.FormTitle>
<Forms.FormText>{plugin.description}</Forms.FormText>
<div style={{ marginTop: 8, marginBottom: 8, width: "fit-content" }}>
<Forms.FormTitle tag="h3" style={{ marginTop: 8, marginBottom: 0 }}>Authors</Forms.FormTitle>
<div style={{ width: "fit-content", marginBottom: 8 }}>
<UserSummaryItem
users={authors}
count={plugin.authors.length}
@ -177,48 +202,65 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
showDefaultAvatarsForNullUsers
showUserPopout
renderMoreUsers={renderMoreUsers}
renderUser={(user: User) => (
<Clickable
className={AvatarStyles.clickableAvatar}
onClick={() => openContributorModal(user)}
>
<img
className={AvatarStyles.avatar}
src={user.getAvatarURL(void 0, 80, true)}
alt={user.username}
title={user.username}
/>
</Clickable>
)}
/>
</div>
</Forms.FormSection>
{!!plugin.settingsAboutComponent && (
<div style={{ marginBottom: 8 }}>
<div className={classes(Margins.bottom8, "vc-text-selectable")}>
<Forms.FormSection>
<ErrorBoundary message="An error occurred while rendering this plugin's custom InfoComponent">
<plugin.settingsAboutComponent />
<plugin.settingsAboutComponent tempSettings={tempSettings} />
</ErrorBoundary>
</Forms.FormSection>
</div>
)}
<Forms.FormSection>
<Forms.FormSection className={Margins.bottom16}>
<Forms.FormTitle tag="h3">Settings</Forms.FormTitle>
{renderSettings()}
</Forms.FormSection>
</ModalContent>
<ModalFooter>
<Flex>
<Button
onClick={onClose}
size={Button.Sizes.SMALL}
color={Button.Colors.RED}
>
Exit Without Saving
</Button>
<Tooltip text="You must fix all errors before saving" shouldShow={!canSubmit()}>
{({ onMouseEnter, onMouseLeave }) => (
<Button
size={Button.Sizes.SMALL}
color={Button.Colors.BRAND}
onClick={saveAndClose}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
disabled={!canSubmit()}
>
Save & Exit
</Button>
)}
</Tooltip>
{hasSettings && <ModalFooter>
<Flex flexDirection="column" style={{ width: "100%" }}>
<Flex style={{ marginLeft: "auto" }}>
<Button
onClick={onClose}
size={Button.Sizes.SMALL}
color={Button.Colors.PRIMARY}
look={Button.Looks.LINK}
>
Cancel
</Button>
<Tooltip text="You must fix all errors before saving" shouldShow={!canSubmit()}>
{({ onMouseEnter, onMouseLeave }) => (
<Button
size={Button.Sizes.SMALL}
color={Button.Colors.BRAND}
onClick={saveAndClose}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
disabled={!canSubmit()}
>
Save & Close
</Button>
)}
</Tooltip>
</Flex>
{saveError && <Text variant="text-md/semibold" style={{ color: "var(--text-danger)" }}>Error while saving: {saveError}</Text>}
</Flex>
</ModalFooter>
</ModalFooter>}
</ModalRoot>
);
}

@ -16,11 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { PluginOptionBoolean } from "../../../utils/types";
import { Forms, React, Select } from "../../../webpack/common";
import { wordsFromCamel, wordsToTitle } from "@utils/text";
import { PluginOptionBoolean } from "@utils/types";
import { Forms, React, Switch } from "@webpack/common";
import { ISettingElementProps } from ".";
export function SettingBooleanComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionBoolean>) {
export function SettingBooleanComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionBoolean>) {
const def = pluginSettings[id] ?? option.default;
const [state, setState] = React.useState(def ?? false);
@ -30,13 +32,8 @@ export function SettingBooleanComponent({ option, pluginSettings, id, onChange,
onError(error !== null);
}, [error]);
const options = [
{ label: "Enabled", value: true, default: def === true },
{ label: "Disabled", value: false, default: typeof def === "undefined" || def === false },
];
function handleChange(newValue: boolean): void {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else {
@ -48,18 +45,17 @@ export function SettingBooleanComponent({ option, pluginSettings, id, onChange,
return (
<Forms.FormSection>
<Forms.FormTitle>{option.description}</Forms.FormTitle>
<Select
isDisabled={option.disabled?.() ?? false}
options={options}
placeholder={option.placeholder ?? "Select an option"}
maxVisibleItems={5}
closeOnSelect={true}
select={handleChange}
isSelected={v => v === state}
serialize={v => String(v)}
<Switch
value={state}
onChange={handleChange}
note={option.description}
disabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
/>
hideBorder
style={{ marginBottom: "0.5em" }}
>
{wordsToTitle(wordsFromCamel(id))}
</Switch>
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}
</Forms.FormSection>
);

@ -16,14 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Guild } from "discord-types/general";
import { PluginOptionComponent } from "@utils/types";
import { ChannelStore, GuildStore,SelectedChannelStore } from "../webpack/common";
import { ISettingElementProps } from ".";
export function getCurrentChannel() {
return ChannelStore.getChannel(SelectedChannelStore.getChannelId());
}
export function getCurrentGuild(): Guild | undefined {
return GuildStore.getGuild(getCurrentChannel()?.guild_id);
export function SettingCustomComponent({ option, onChange, onError }: ISettingElementProps<PluginOptionComponent>) {
return option.component({ setValue: onChange, setError: onError, option });
}

@ -16,13 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { OptionType, PluginOptionNumber } from "../../../utils/types";
import { Forms, React, TextInput } from "../../../webpack/common";
import { OptionType, PluginOptionNumber } from "@utils/types";
import { Forms, React, TextInput } from "@webpack/common";
import { ISettingElementProps } from ".";
const MAX_SAFE_NUMBER = BigInt(Number.MAX_SAFE_INTEGER);
export function SettingNumericComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionNumber>) {
export function SettingNumericComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionNumber>) {
function serialize(value: any) {
if (option.type === OptionType.BIGINT) return BigInt(value);
return Number(value);
@ -36,10 +37,13 @@ export function SettingNumericComponent({ option, pluginSettings, id, onChange,
}, [error]);
function handleChange(newValue) {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
setError(null);
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else if (option.type === OptionType.NUMBER && BigInt(newValue) >= MAX_SAFE_NUMBER) {
if (option.type === OptionType.NUMBER && BigInt(newValue) >= MAX_SAFE_NUMBER) {
setState(`${Number.MAX_SAFE_INTEGER}`);
onChange(serialize(newValue));
} else {
@ -57,7 +61,7 @@ export function SettingNumericComponent({ option, pluginSettings, id, onChange,
value={state}
onChange={handleChange}
placeholder={option.placeholder ?? "Enter a number"}
disabled={option.disabled?.() ?? false}
disabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
/>
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}

@ -16,11 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { PluginOptionSelect } from "../../../utils/types";
import { Forms, React, Select } from "../../../webpack/common";
import { PluginOptionSelect } from "@utils/types";
import { Forms, React, Select } from "@webpack/common";
import { ISettingElementProps } from ".";
export function SettingSelectComponent({ option, pluginSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) {
export function SettingSelectComponent({ option, pluginSettings, definedSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) {
const def = pluginSettings[id] ?? option.options?.find(o => o.default)?.value;
const [state, setState] = React.useState<any>(def ?? null);
@ -31,10 +32,11 @@ export function SettingSelectComponent({ option, pluginSettings, onChange, onErr
}, [error]);
function handleChange(newValue) {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else {
setError(null);
setState(newValue);
onChange(newValue);
}
@ -44,7 +46,7 @@ export function SettingSelectComponent({ option, pluginSettings, onChange, onErr
<Forms.FormSection>
<Forms.FormTitle>{option.description}</Forms.FormTitle>
<Select
isDisabled={option.disabled?.() ?? false}
isDisabled={option.disabled?.call(definedSettings) ?? false}
options={option.options}
placeholder={option.placeholder ?? "Select an option"}
maxVisibleItems={5}

@ -16,8 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { PluginOptionSlider } from "../../../utils/types";
import { Forms, React, Slider } from "../../../webpack/common";
import { PluginOptionSlider } from "@utils/types";
import { Forms, React, Slider } from "@webpack/common";
import { ISettingElementProps } from ".";
export function makeRange(start: number, end: number, step = 1) {
@ -28,7 +29,7 @@ export function makeRange(start: number, end: number, step = 1) {
return ranges;
}
export function SettingSliderComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionSlider>) {
export function SettingSliderComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionSlider>) {
const def = pluginSettings[id] ?? option.default;
const [error, setError] = React.useState<string | null>(null);
@ -38,7 +39,7 @@ export function SettingSliderComponent({ option, pluginSettings, id, onChange, o
}, [error]);
function handleChange(newValue: number): void {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else {
@ -51,7 +52,7 @@ export function SettingSliderComponent({ option, pluginSettings, id, onChange, o
<Forms.FormSection>
<Forms.FormTitle>{option.description}</Forms.FormTitle>
<Slider
disabled={option.disabled?.() ?? false}
disabled={option.disabled?.call(definedSettings) ?? false}
markers={option.markers}
minValue={option.markers[0]}
maxValue={option.markers[option.markers.length - 1]}

@ -16,11 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { PluginOptionString } from "../../../utils/types";
import { Forms, React, TextInput } from "../../../webpack/common";
import { PluginOptionString } from "@utils/types";
import { Forms, React, TextInput } from "@webpack/common";
import { ISettingElementProps } from ".";
export function SettingInputComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionString>) {
export function SettingTextComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionString>) {
const [state, setState] = React.useState(pluginSettings[id] ?? option.default ?? null);
const [error, setError] = React.useState<string | null>(null);
@ -29,13 +30,13 @@ export function SettingInputComponent({ option, pluginSettings, id, onChange, on
}, [error]);
function handleChange(newValue) {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else {
setState(newValue);
onChange(newValue);
}
else setError(null);
setState(newValue);
onChange(newValue);
}
return (
@ -46,7 +47,7 @@ export function SettingInputComponent({ option, pluginSettings, id, onChange, on
value={state}
onChange={handleChange}
placeholder={option.placeholder ?? "Enter a value"}
disabled={option.disabled?.() ?? false}
disabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
/>
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}

Some files were not shown because too many files have changed in this diff Show More