From 04e6a9d3acad499467a0766225b61de1ab3318eb Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 9 Apr 2025 08:15:32 -0400 Subject: [PATCH 001/198] Restrict ClipsEnhancements --- README.md | 2 +- .../index.tsx | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/equicordplugins/{clipsEnhancements => clipsEnhancements.discordDesktop}/index.tsx (100%) diff --git a/README.md b/README.md index 8d061fa3..19f7535e 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - CharacterCounter by Creations & Panniku - CleanChannelName by AutumnVN - ClientSideBlock by Samwich -- ClipsEnhancements by niko - CommandPalette by Ethan - CopyUserMention by Cortex & castdrian - CustomSounds by TheKodeToad & SpikeHD @@ -184,6 +183,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Discord Desktop Only +- ClipsEnhancements by niko - MediaDownloader by Colorman - StatusWhilePlaying by thororen diff --git a/src/equicordplugins/clipsEnhancements/index.tsx b/src/equicordplugins/clipsEnhancements.discordDesktop/index.tsx similarity index 100% rename from src/equicordplugins/clipsEnhancements/index.tsx rename to src/equicordplugins/clipsEnhancements.discordDesktop/index.tsx From f6f0e60b7e2da12951157717553975816cba6fd9 Mon Sep 17 00:00:00 2001 From: Crxaw <48805031+sitescript@users.noreply.github.com> Date: Wed, 9 Apr 2025 13:27:05 +0100 Subject: [PATCH 002/198] Added LastActive (#224) * Added LastActive LastActive is a plugin that fetches your messages to jump to them. (https://discord.com/channels/1173279886065029291/1359354001560178872) * Added to README * Fix Count --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> --- README.md | 3 +- src/equicordplugins/lastActive/index.tsx | 146 +++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/equicordplugins/lastActive/index.tsx diff --git a/README.md b/README.md index 19f7535e..11c3bb09 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-163 additional plugins +164 additional plugins ### All Platforms @@ -172,6 +172,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - Woof by Samwich - WriteUpperCase by Samwich & KrystalSkull - YoutubeDescription by arHSM +- LastActive by Crxa ### Web Only diff --git a/src/equicordplugins/lastActive/index.tsx b/src/equicordplugins/lastActive/index.tsx new file mode 100644 index 00000000..c07e4367 --- /dev/null +++ b/src/equicordplugins/lastActive/index.tsx @@ -0,0 +1,146 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { Menu, NavigationRouter, Toasts } from "@webpack/common"; +const MessageStore = findByPropsLazy("getMessages", "getMessage"); +const ChannelStore = findByPropsLazy("getChannel", "getDMFromUserId"); +const UserStore = findByPropsLazy("getUser", "getCurrentUser"); +const MessageActions = findByPropsLazy("fetchMessages", "searchMessages"); + +async function findLastMessageFromUser(channelId: string, userId: string) { + try { + + if (!MessageStore || !MessageActions) { + Toasts.show({ + type: Toasts.Type.FAILURE, + message: "Required Discord modules not found.", + id: Toasts.genId() + }); + return null; + } + const messageCollection = MessageStore.getMessages(channelId); + let messages = messageCollection?.toArray() || []; + let userMessage = messages.filter(m => m?.author?.id === userId).pop(); + if (userMessage) return userMessage.id; + try { + await MessageActions.fetchMessages({ + channelId: channelId, + limit: 50 + }); + + const updatedCollection = MessageStore.getMessages(channelId); + messages = updatedCollection?.toArray() || []; + userMessage = messages.filter(m => m?.author?.id === userId).pop(); + + if (userMessage) return userMessage.id; + } catch (fetchError) { + console.error("Error fetching messages:", fetchError); + } + + Toasts.show({ + type: Toasts.Type.FAILURE, + message: "Couldn't find any recent messages from this user.", + id: Toasts.genId() + }); + return null; + } catch (error) { + console.error("Error finding last message:", error); + Toasts.show({ + type: Toasts.Type.FAILURE, + message: "Failed to find messages. Check console for details.", + id: Toasts.genId() + }); + return null; + } +} +async function jumpToLastActive(channel: any, targetUserId?: string) { + try { + if (!channel) { + Toasts.show({ + type: Toasts.Type.FAILURE, + message: "Channel information not available.", + id: Toasts.genId() + }); + return; + } + const guildId = channel.guild_id !== null ? channel.guild_id : "@me"; + const channelId = channel.id; + let userId: string; + if (targetUserId) { + + userId = targetUserId; + } else { + if (!UserStore?.getCurrentUser) { + Toasts.show({ + type: Toasts.Type.FAILURE, + message: "Could not determine user. Try again later.", + id: Toasts.genId() + }); + return; + } + const currentUser = UserStore.getCurrentUser(); + if (!currentUser || !currentUser.id) { + Toasts.show({ + type: Toasts.Type.FAILURE, + message: "Could not determine current user. Try again later.", + id: Toasts.genId() + }); + return; + } + userId = currentUser.id; + } + const messageId = await findLastMessageFromUser(channelId, userId); + if (messageId) { + const url = `/channels/${guildId}/${channelId}/${messageId}`; + NavigationRouter.transitionTo(url); + } + } catch (error) { + console.error("Error in jumpToLastActive:", error); + Toasts.show({ + type: Toasts.Type.FAILURE, + message: "Failed to jump to message. Check console for details.", + id: Toasts.genId() + }); + } +} +const ChannelContextMenuPatch: NavContextMenuPatchCallback = (children, { channel }) => { + children.push( + Jump to Your Last Message} + action={() => { + jumpToLastActive(channel); + }} + /> + ); +}; +const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user, channel }) => { + if (!channel || !user?.id) return; + + children.push( + Jump to User's Last Message} + action={() => { + jumpToLastActive(channel, user.id); + }} + /> + ); +}; +export default definePlugin({ + name: "LastActive", + description: "A plugin to jump to last active message from yourself or another user in a channel/server.", + authors: [EquicordDevs.Crxa], + contextMenus: { + "channel-context": ChannelContextMenuPatch, + "user-context": UserContextMenuPatch, + "thread-context": ChannelContextMenuPatch + } +}); From ca065331c2683d1faa248bf1a89da14938ed733a Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 9 Apr 2025 09:00:20 -0400 Subject: [PATCH 003/198] QuestCompleter : Move Header To Titlebar --- src/equicordplugins/questCompleter/index.tsx | 103 ++++++++++--------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/src/equicordplugins/questCompleter/index.tsx b/src/equicordplugins/questCompleter/index.tsx index 15a91eba..ff97caa5 100644 --- a/src/equicordplugins/questCompleter/index.tsx +++ b/src/equicordplugins/questCompleter/index.tsx @@ -19,36 +19,19 @@ import "@equicordplugins/_misc/styles.css"; import { showNotification } from "@api/Notifications"; -import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { getTheme, Theme } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { findByProps, findComponentByCodeLazy } from "@webpack"; -import { Button, FluxDispatcher, Forms, NavigationRouter, RestAPI, Tooltip, UserStore } from "@webpack/common"; +import { Button, ChannelStore, FluxDispatcher, Forms, GuildChannelStore, NavigationRouter, RestAPI, Tooltip, UserStore } from "@webpack/common"; -const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"'); -const isApp = navigator.userAgent.includes("Electron/"); +const isApp = typeof DiscordNative !== "undefined"; import "./style.css"; import { definePluginSettings } from "@api/Settings"; -const ToolBarQuestsIcon = findComponentByCodeLazy("1 0 1 1.73Z"); - -function ToolBarHeader() { - return ( - - - - - ); -} +const QuestIcon = findComponentByCodeLazy("10.47a.76.76"); async function openCompleteQuestUI() { const ApplicationStreamingStore = findByProps("getStreamerActiveStreamMetadata"); @@ -72,7 +55,7 @@ async function openCompleteQuestUI() { const applicationId = quest.config.application.id; const applicationName = quest.config.application.name; - const taskName = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP"].find(x => quest.config.taskConfig.tasks[x] != null); + const taskName = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY"].find(x => quest.config.taskConfig.tasks[x] != null); // @ts-ignore const secondsNeeded = quest.config.taskConfig.tasks[taskName].target; // @ts-ignore @@ -118,8 +101,6 @@ async function openCompleteQuestUI() { RestAPI.get({ url: `/applications/public?application_ids=${applicationId}` }).then(res => { const appData = res.body[0]; const exeName = appData.executables.find(x => x.os === "win32").name.replace(">", ""); - - const games = RunningGameStore.getRunningGames(); const fakeGame = { cmdLine: `C:\\Program Files\\${appData.name}\\${exeName}`, exeName, @@ -133,8 +114,15 @@ async function openCompleteQuestUI() { processName: appData.name, start: Date.now(), }; - games.push(fakeGame); - FluxDispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: [], added: [fakeGame], games: games }); + const realGames = RunningGameStore.getRunningGames(); + const fakeGames = [fakeGame]; + const realGetRunningGames = RunningGameStore.getRunningGames; + const realGetGameForPID = RunningGameStore.getGameForPID; + RunningGameStore.getRunningGames = () => fakeGames; + RunningGameStore.getGameForPID = pid => fakeGames.find(x => x.pid === pid); + FluxDispatcher.dispatch({ + type: "RUNNING_GAMES_CHANGE", removed: realGames, added: [fakeGame], games: fakeGames + }); const fn = data => { const progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value); @@ -151,11 +139,9 @@ async function openCompleteQuestUI() { icon: icon, }); - const idx = games.indexOf(fakeGame); - if (idx > -1) { - games.splice(idx, 1); - FluxDispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: [] }); - } + RunningGameStore.getRunningGames = realGetRunningGames; + RunningGameStore.getGameForPID = realGetGameForPID; + FluxDispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: [] }); FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn); } }; @@ -211,6 +197,36 @@ async function openCompleteQuestUI() { }; FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn); console.log(`Spoofed your stream to ${applicationName}.`); + } else if (taskName === "PLAY_ACTIVITY") { + const channelId = ChannelStore.getSortedPrivateChannels()[0]?.id ?? Object.values(GuildChannelStore.getAllGuilds() as any[]).find(x => x != null && x.VOCAL.length > 0).VOCAL[0].channel.id; + const streamKey = `call:${channelId}:1`; + + const fn = async () => { + + while (true) { + const res = await RestAPI.post({ url: `/quests/${quest.id}/heartbeat`, body: { stream_key: streamKey, terminal: false } }); + const progress = res.body.progress.PLAY_ACTIVITY.value; + showNotification({ + title: `${applicationName} - Quest Completer`, + body: `Current progress: ${progress}/${secondsNeeded} seconds.`, + icon: icon, + }); + + await new Promise(resolve => setTimeout(resolve, 20 * 1000)); + + if (progress >= secondsNeeded) { + await RestAPI.post({ url: `/quests/${quest.id}/heartbeat`, body: { stream_key: streamKey, terminal: true } }); + break; + } + } + + showNotification({ + title: `${applicationName} - Quest Completer`, + body: "Quest Completed.", + icon: icon, + }); + }; + fn(); } return; } @@ -244,10 +260,10 @@ export default definePlugin({ } }, { - find: "toolbar:function", + find: "AppTitleBar", replacement: { - match: /(function \i\(\i\){)(.{1,500}toolbar.{1,500}mobileToolbar)/, - replace: "$1$self.toolbarAction(arguments[0]);$2" + match: /(?<=trailing:.{0,70}\(\i\.Fragment,{children:\[.*?)\]/, + replace: ",$self.renderQuestButton()]" } }, { @@ -263,32 +279,17 @@ export default definePlugin({ return ( {tooltipProps => ( - )} ); }, openCompleteQuestUI, - toolbarAction(e) { - if (Array.isArray(e.toolbar)) - return e.toolbar.push( - - - - ); - - e.toolbar = [ - - - , - e.toolbar, - ]; - } }); From 16763a7cd45f0b60d922a924ee90fe3b2b3ea890 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:25:29 -0400 Subject: [PATCH 004/198] QuestCompleter: Remove Old Patches --- src/equicordplugins/questCompleter/index.tsx | 30 +------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/src/equicordplugins/questCompleter/index.tsx b/src/equicordplugins/questCompleter/index.tsx index ff97caa5..83b62788 100644 --- a/src/equicordplugins/questCompleter/index.tsx +++ b/src/equicordplugins/questCompleter/index.tsx @@ -21,7 +21,7 @@ import "@equicordplugins/_misc/styles.css"; import { showNotification } from "@api/Notifications"; import { Devs } from "@utils/constants"; import { getTheme, Theme } from "@utils/discord"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin from "@utils/types"; import { findByProps, findComponentByCodeLazy } from "@webpack"; import { Button, ChannelStore, FluxDispatcher, Forms, GuildChannelStore, NavigationRouter, RestAPI, Tooltip, UserStore } from "@webpack/common"; @@ -29,8 +29,6 @@ const isApp = typeof DiscordNative !== "undefined"; import "./style.css"; -import { definePluginSettings } from "@api/Settings"; - const QuestIcon = findComponentByCodeLazy("10.47a.76.76"); async function openCompleteQuestUI() { @@ -232,15 +230,6 @@ async function openCompleteQuestUI() { } } -const settings = definePluginSettings({ - clickableQuestDiscovery: { - type: OptionType.BOOLEAN, - description: "Makes the quest button in discovery clickable", - restartNeeded: true, - default: false - } -}); - export default definePlugin({ name: "QuestCompleter", description: "A plugin to complete quests without having the game installed.", @@ -250,29 +239,13 @@ export default definePlugin({ Game Quests do not work on Equibop/Web Platforms. Only Video Quests do. , - settings, patches: [ - { - find: "\"invite-button\"", - replacement: { - match: /\i&&(\i\i\.push).{0,50}"current-speaker"/, - replace: "$1($self.renderQuestButton()),$&" - } - }, { find: "AppTitleBar", replacement: { match: /(?<=trailing:.{0,70}\(\i\.Fragment,{children:\[.*?)\]/, replace: ",$self.renderQuestButton()]" } - }, - { - find: "M7.5 21.7a8.95 8.95 0 0 1 9 0 1 1 0 0 0 1-1.73c", - replacement: { - match: /(?<=className:\i\}\))/, - replace: ",onClick:()=>$self.openCompleteQuestUI()" - }, - predicate: () => settings.store.clickableQuestDiscovery } ], renderQuestButton() { @@ -291,5 +264,4 @@ export default definePlugin({ ); }, - openCompleteQuestUI, }); From c17154e7cda34393d61b247b32f2c71bfbff12c9 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 9 Apr 2025 23:24:22 -0400 Subject: [PATCH 005/198] Stable Fixes --- src/equicordplugins/lastActive/index.tsx | 32 +---------------------- src/plugins/_api/dynamicImageModalApi.ts | 7 ----- src/plugins/imageZoom/index.tsx | 8 ------ src/plugins/roleColorEverywhere/index.tsx | 2 +- src/utils/discord.css | 24 ----------------- src/utils/discord.tsx | 3 --- src/utils/modal.tsx | 12 +++------ 7 files changed, 5 insertions(+), 83 deletions(-) delete mode 100644 src/utils/discord.css diff --git a/src/equicordplugins/lastActive/index.tsx b/src/equicordplugins/lastActive/index.tsx index c07e4367..4325bee6 100644 --- a/src/equicordplugins/lastActive/index.tsx +++ b/src/equicordplugins/lastActive/index.tsx @@ -7,24 +7,10 @@ import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; -import { Menu, NavigationRouter, Toasts } from "@webpack/common"; -const MessageStore = findByPropsLazy("getMessages", "getMessage"); -const ChannelStore = findByPropsLazy("getChannel", "getDMFromUserId"); -const UserStore = findByPropsLazy("getUser", "getCurrentUser"); -const MessageActions = findByPropsLazy("fetchMessages", "searchMessages"); +import { Menu, MessageActions, MessageStore, NavigationRouter, Toasts, UserStore } from "@webpack/common"; async function findLastMessageFromUser(channelId: string, userId: string) { try { - - if (!MessageStore || !MessageActions) { - Toasts.show({ - type: Toasts.Type.FAILURE, - message: "Required Discord modules not found.", - id: Toasts.genId() - }); - return null; - } const messageCollection = MessageStore.getMessages(channelId); let messages = messageCollection?.toArray() || []; let userMessage = messages.filter(m => m?.author?.id === userId).pop(); @@ -77,23 +63,7 @@ async function jumpToLastActive(channel: any, targetUserId?: string) { userId = targetUserId; } else { - if (!UserStore?.getCurrentUser) { - Toasts.show({ - type: Toasts.Type.FAILURE, - message: "Could not determine user. Try again later.", - id: Toasts.genId() - }); - return; - } const currentUser = UserStore.getCurrentUser(); - if (!currentUser || !currentUser.id) { - Toasts.show({ - type: Toasts.Type.FAILURE, - message: "Could not determine current user. Try again later.", - id: Toasts.genId() - }); - return; - } userId = currentUser.id; } const messageId = await findLastMessageFromUser(channelId, userId); diff --git a/src/plugins/_api/dynamicImageModalApi.ts b/src/plugins/_api/dynamicImageModalApi.ts index 759ef001..d91a5a93 100644 --- a/src/plugins/_api/dynamicImageModalApi.ts +++ b/src/plugins/_api/dynamicImageModalApi.ts @@ -13,13 +13,6 @@ export default definePlugin({ authors: [Devs.sadan, Devs.Nuckyz], description: "Allows you to omit either width or height when opening an image modal", patches: [ - { - find: ".contain,SCALE_DOWN:", - replacement: { - match: /(?<="IMAGE"===\i\?)\i(?=\?)/, - replace: "true" - } - }, { find: ".dimensionlessImage,", replacement: { diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index 1a17b616..25a0ab7d 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -162,14 +162,6 @@ export default definePlugin({ managedStyle, patches: [ - { - find: ".contain,SCALE_DOWN:", - replacement: { - match: /imageClassName:/, - replace: `id:"${ELEMENT_ID}",$&` - } - }, - { find: ".dimensionlessImage,", replacement: [ diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index ff3911f5..f16161fa 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -85,7 +85,7 @@ export default definePlugin({ find: ".USER_MENTION)", replacement: [ { - match: /(?<=onContextMenu:\i,color:)\i(?=\},\i\),\{children)(?<=user:(\i),channel:(\i).{0,500}?)/, + match: /(?<=onContextMenu:\i,color:)\i(?=,onClick)(?<=user:(\i),channel:(\i).+?)/, replace: "$self.getColorInt($1?.id,$2?.id)", } ], diff --git a/src/utils/discord.css b/src/utils/discord.css deleted file mode 100644 index 8d4f811a..00000000 --- a/src/utils/discord.css +++ /dev/null @@ -1,24 +0,0 @@ - -.vc-position-inherit { - position: inherit; -} - -/** - * copy pasted from discord css. not really webpack-findable since it's the only class in the module -**/ - -.vc-image-modal { - background: transparent !important; - box-shadow: none !important; - display: flex; - justify-content: center; - align-items: center; - border-radius: 0; -} - -@media(width <= 485px) { - .vc-image-modal { - overflow: visible; - overflow: initial; - } -} diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index ec96d0d4..fff6d9fc 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -143,9 +143,6 @@ export function sendMessage( */ export function openImageModal(item: Except, mediaModalProps?: Omit) { return openMediaModal({ - className: "vc-image-modal", - fit: "vc-position-inherit", - shouldAnimateCarousel: true, items: [{ type: "IMAGE", original: item.original ?? item.url, diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index d06e5803..eebdb95e 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { filters, findModuleId, mapMangledModuleLazy, proxyLazyWebpack, wreq } from "@webpack"; +import { filters, findByCodeLazy, mapMangledModuleLazy } from "@webpack"; import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react"; import { LazyComponent } from "./react"; @@ -138,16 +138,10 @@ export type MediaModalProps = { fit?: string; shouldRedactExplicitContent?: boolean; shouldHideMediaOptions?: boolean; - shouldAnimateCarousel?: boolean; }; -export const openMediaModal: (props: MediaModalProps) => void = proxyLazyWebpack(() => { - const mediaModalKeyModuleId = findModuleId('"Zoomed Media Modal"'); - if (mediaModalKeyModuleId == null) return; - - const openMediaModalModule = wreq(findModuleId(mediaModalKeyModuleId, "modalKey:") as any); - return Object.values(openMediaModalModule).find(v => String(v).includes("modalKey:")); -}); +// modal key: "Media Viewer Modal" +export const openMediaModal: (props: MediaModalProps) => void = findByCodeLazy("hasMediaOptions", "shouldHideMediaOptions"); interface ModalAPI { /** From ffb8f7a7c95a9168eb06ee4c11b239ee68d9d329 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 9 Apr 2025 23:27:48 -0400 Subject: [PATCH 006/198] Canary Fixes --- src/equicordplugins/customUserColors/index.tsx | 2 +- src/equicordplugins/statusPresets/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index 9898fe56..ce9e66e5 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -106,7 +106,7 @@ export default definePlugin({ { find: "PrivateChannel.renderAvatar", replacement: { - match: /(highlighted:\i,)/, + match: /(subText:\i\(\),)/, replace: "$1style:{color:`${$self.colorDMList(arguments[0])}`}," }, predicate: () => settings.store.dmList, diff --git a/src/equicordplugins/statusPresets/index.tsx b/src/equicordplugins/statusPresets/index.tsx index 56849af6..293bb3da 100644 --- a/src/equicordplugins/statusPresets/index.tsx +++ b/src/equicordplugins/statusPresets/index.tsx @@ -58,7 +58,7 @@ interface DiscordStatus { const StatusStyles = findByPropsLazy("statusItem"); // TODO: find clearCustomStatusHint original css/svg or replace const PMenu = findComponentByCodeLazy(".menuItemLabel", ".menuItemInner"); -const EmojiComponent = findComponentByCodeLazy(/\.translateSurrogatesToInlineEmoji\(\i.\i\),/); +const EmojiComponent = findComponentByCodeLazy(/\.translateSurrogatesToInlineEmoji\(\i\.name\);/); const CustomStatusSettings = getUserSettingLazy("status", "customStatus")!; const StatusModule = proxyLazy(() => { From 57100dac979462d4812649142baddcc1bf78b7f0 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 9 Apr 2025 23:29:26 -0400 Subject: [PATCH 007/198] Fix Updates --- src/utils/discord.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index fff6d9fc..ba60d98a 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -import "./discord.css"; - import { MessageObject } from "@api/MessageEvents"; import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { Channel, Guild, Message, User } from "discord-types/general"; From eff754ecfaad2e3e9e81fa5293400f12b3e729f4 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 10 Apr 2025 07:18:10 -0400 Subject: [PATCH 008/198] Fix ClientSideBlock Crashing --- src/equicordplugins/clientSideBlock/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/clientSideBlock/index.tsx b/src/equicordplugins/clientSideBlock/index.tsx index e5993d72..206a4cf4 100644 --- a/src/equicordplugins/clientSideBlock/index.tsx +++ b/src/equicordplugins/clientSideBlock/index.tsx @@ -152,8 +152,8 @@ export default definePlugin({ { find: "._areActivitiesExperimentallyHidden=(", replacement: { - match: /BOOST_GEM_ICON\}\}\)\)\};/, - replace: "$&if($self.shouldHideUser(this.props.user.id, this.props.channel.id)) return null; " + match: /(?<=user:(\i),guildId:\i,channel:(\i).*?)BOOST_GEM_ICON\}\}\)\)\};/, + replace: "$&if($self.shouldHideUser($1.id, $2.id)) return null; " } }, // stop the role header from displaying if all users with that role are hidden (wip sorta) From 8ad33db6c28a579ac66f4676f6896c34466c168f Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:44:04 -0400 Subject: [PATCH 009/198] fix plugins using the image modal (#3368) --- src/plugins/_api/dynamicImageModalApi.ts | 7 ------- src/plugins/imageZoom/index.tsx | 8 -------- src/utils/discord.css | 23 ----------------------- src/utils/discord.tsx | 5 ----- src/utils/modal.tsx | 12 +++--------- 5 files changed, 3 insertions(+), 52 deletions(-) delete mode 100644 src/utils/discord.css diff --git a/src/plugins/_api/dynamicImageModalApi.ts b/src/plugins/_api/dynamicImageModalApi.ts index 759ef001..d91a5a93 100644 --- a/src/plugins/_api/dynamicImageModalApi.ts +++ b/src/plugins/_api/dynamicImageModalApi.ts @@ -13,13 +13,6 @@ export default definePlugin({ authors: [Devs.sadan, Devs.Nuckyz], description: "Allows you to omit either width or height when opening an image modal", patches: [ - { - find: ".contain,SCALE_DOWN:", - replacement: { - match: /(?<="IMAGE"===\i\?)\i(?=\?)/, - replace: "true" - } - }, { find: ".dimensionlessImage,", replacement: { diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index 1a17b616..25a0ab7d 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -162,14 +162,6 @@ export default definePlugin({ managedStyle, patches: [ - { - find: ".contain,SCALE_DOWN:", - replacement: { - match: /imageClassName:/, - replace: `id:"${ELEMENT_ID}",$&` - } - }, - { find: ".dimensionlessImage,", replacement: [ diff --git a/src/utils/discord.css b/src/utils/discord.css deleted file mode 100644 index 746fb564..00000000 --- a/src/utils/discord.css +++ /dev/null @@ -1,23 +0,0 @@ -.vc-position-inherit { - position: inherit; -} - -/** - * copy pasted from discord css. not really webpack-findable since it's the only class in the module -**/ - -.vc-image-modal { - background: transparent !important; - box-shadow: none !important; - display: flex; - justify-content: center; - align-items: center; - border-radius: 0; -} - -@media(width <= 485px) { - .vc-image-modal { - overflow: visible; - overflow: initial; - } -} diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index ec96d0d4..ba60d98a 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -import "./discord.css"; - import { MessageObject } from "@api/MessageEvents"; import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { Channel, Guild, Message, User } from "discord-types/general"; @@ -143,9 +141,6 @@ export function sendMessage( */ export function openImageModal(item: Except, mediaModalProps?: Omit) { return openMediaModal({ - className: "vc-image-modal", - fit: "vc-position-inherit", - shouldAnimateCarousel: true, items: [{ type: "IMAGE", original: item.original ?? item.url, diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index d06e5803..eebdb95e 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { filters, findModuleId, mapMangledModuleLazy, proxyLazyWebpack, wreq } from "@webpack"; +import { filters, findByCodeLazy, mapMangledModuleLazy } from "@webpack"; import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react"; import { LazyComponent } from "./react"; @@ -138,16 +138,10 @@ export type MediaModalProps = { fit?: string; shouldRedactExplicitContent?: boolean; shouldHideMediaOptions?: boolean; - shouldAnimateCarousel?: boolean; }; -export const openMediaModal: (props: MediaModalProps) => void = proxyLazyWebpack(() => { - const mediaModalKeyModuleId = findModuleId('"Zoomed Media Modal"'); - if (mediaModalKeyModuleId == null) return; - - const openMediaModalModule = wreq(findModuleId(mediaModalKeyModuleId, "modalKey:") as any); - return Object.values(openMediaModalModule).find(v => String(v).includes("modalKey:")); -}); +// modal key: "Media Viewer Modal" +export const openMediaModal: (props: MediaModalProps) => void = findByCodeLazy("hasMediaOptions", "shouldHideMediaOptions"); interface ModalAPI { /** From 3dd58c255120661cc79214544fb3e5f108e8e7b8 Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:45:22 -0400 Subject: [PATCH 010/198] fix RoleColorEverywhere (#3370) --- src/plugins/roleColorEverywhere/index.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index ffa2b5a2..b81a0cce 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -84,13 +84,7 @@ export default definePlugin({ find: ".USER_MENTION)", replacement: [ { - // FIXME(Bundler spread transform related): Remove old compatiblity once enough time has passed, if they don't revert - match: /onContextMenu:\i,color:\i,\.\.\.\i(?=,children:)(?<=user:(\i),channel:(\i).{0,500}?)/, - replace: "$&,color:$self.getColorInt($1?.id,$2?.id)", - noWarn: true - }, - { - match: /(?<=onContextMenu:\i,color:)\i(?=\},\i\),\{children)(?<=user:(\i),channel:(\i).{0,500}?)/, + match: /(?<=onContextMenu:\i,color:)\i(?=,onClick)(?<=user:(\i),channel:(\i).+?)/, replace: "$self.getColorInt($1?.id,$2?.id)", } ], From 6f23c9e67a4c706435a73339aec3383be9022e76 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 10 Apr 2025 16:56:15 +0200 Subject: [PATCH 011/198] ReadAllNotificationsButton: adjust look for new ui --- src/plugins/readAllNotificationsButton/index.tsx | 1 + src/plugins/readAllNotificationsButton/style.css | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/readAllNotificationsButton/index.tsx b/src/plugins/readAllNotificationsButton/index.tsx index 7a6737a8..419e93af 100644 --- a/src/plugins/readAllNotificationsButton/index.tsx +++ b/src/plugins/readAllNotificationsButton/index.tsx @@ -72,6 +72,7 @@ const ReadAllButton = () => ( ))} - +
+ +
); } @@ -59,10 +61,10 @@ const settings = definePluginSettings({ reasons: { description: "Your custom reasons", type: OptionType.COMPONENT, - default: [""], + default: [] as string[], component: ReasonsComponent, }, - textInputDefault: { + isTextInputDefault: { type: OptionType.BOOLEAN, description: 'Shows a text input instead of a select menu by default. (Equivalent to clicking the "Other" option)' } @@ -74,9 +76,9 @@ export default definePlugin({ authors: [Devs.Inbestigator], patches: [ { - find: "#{intl::BAN_MULTIPLE_CONFIRM_TITLE}", + find: "#{intl::BAN_REASON_OPTION_SPAM_ACCOUNT}", replacement: [{ - match: /\[\{name:\i\.\i\.string\(\i\.\i#{intl::BAN_REASON_OPTION_SPAM_ACCOUNT}\).+?\}\]/, + match: /\[(\{((name|value):\i\.\i\.string\(\i\.\i\.\i\),?){2}\},?){3}\]/, replace: "$self.getReasons()" }, { @@ -86,17 +88,16 @@ export default definePlugin({ } ], getReasons() { - const reasons = settings.store.reasons.length - ? settings.store.reasons + const storedReasons = settings.store.reasons.filter((r: string) => r.trim()); + const reasons: string[] = storedReasons.length + ? storedReasons : [ getIntlMessage("BAN_REASON_OPTION_SPAM_ACCOUNT"), getIntlMessage("BAN_REASON_OPTION_HACKED_ACCOUNT"), - getIntlMessage("BAN_REASON_OPTION_BREAKING_RULES") + getIntlMessage("BAN_REASON_OPTION_BREAKING_RULES"), ]; return reasons.map(s => ({ name: s, value: s })); }, - getDefaultState() { - return settings.store.textInputDefault ? 1 : 0; - }, + getDefaultState: () => settings.store.isTextInputDefault ? 1 : 0, settings, }); diff --git a/src/equicordplugins/betterBanReasons/style.css b/src/equicordplugins/betterBanReasons/style.css index 02fb4bbf..a1ba64ea 100644 --- a/src/equicordplugins/betterBanReasons/style.css +++ b/src/equicordplugins/betterBanReasons/style.css @@ -1,11 +1,31 @@ .vc-bbr-reason-wrapper { display: grid; - padding: 0; - padding-bottom: 0.5rem; - gap: 0.5rem; - grid-template-columns: 6fr 1fr; + padding-bottom: 12px; + gap: 4px 12px; + align-items: center; + grid-template-columns: 1fr 24px; } .vc-bbr-remove-button { - height: 100%; + color: var(--text-muted); +} + +.vc-bbr-remove-button:hover { + color: var(--button-danger-background-hover); +} + +.vc-bbr-add-button { + justify-content: start !important; + border: 0; + padding: 2px 12px; + color: var(--text-muted) !important; + font-size: 16px; +} + +.vc-bbr-add-button div { + display: flex; + justify-content: start; + align-items: center; + gap: 12px; + margin: 0 !important; } diff --git a/src/plugins/banger/index.ts b/src/plugins/banger/index.ts index f13fd351..504091e4 100644 --- a/src/plugins/banger/index.ts +++ b/src/plugins/banger/index.ts @@ -36,7 +36,7 @@ export default definePlugin({ settings, patches: [ { - find: "#{intl::BAN_CONFIRM_TITLE}", + find: "#{intl::BAN_REASON_OPTION_SPAM_ACCOUNT}", replacement: { match: /src:\i\("?\d+"?\)/g, replace: "src:$self.source" From 026d8d50aaece5e216ce667f0445bda172042939 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 10 Apr 2025 21:46:29 -0400 Subject: [PATCH 015/198] Fix Lint --- src/equicordplugins/betterBanReasons/{style.css => styles.css} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/equicordplugins/betterBanReasons/{style.css => styles.css} (100%) diff --git a/src/equicordplugins/betterBanReasons/style.css b/src/equicordplugins/betterBanReasons/styles.css similarity index 100% rename from src/equicordplugins/betterBanReasons/style.css rename to src/equicordplugins/betterBanReasons/styles.css From 3c3712ce73e6c0f30d8dd86b509dbce5a51331a0 Mon Sep 17 00:00:00 2001 From: "Indiana Jone (Indi)" <150982280+KrstlSkll69@users.noreply.github.com> Date: Thu, 10 Apr 2025 21:47:02 -0400 Subject: [PATCH 016/198] Update Quest-Completer CSS (#227) --- src/equicordplugins/questCompleter/style.css | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/questCompleter/style.css b/src/equicordplugins/questCompleter/style.css index 5e4f7b7b..ecdccebb 100644 --- a/src/equicordplugins/questCompleter/style.css +++ b/src/equicordplugins/questCompleter/style.css @@ -1,3 +1,12 @@ -.vc-quest-completer-icon:hover{ - filter: brightness(10); +.vc-quest-completer-icon{ + bottom: -4px; + padding: 0; + width: var(--space-32); + min-width: 0; + height: var(--space-32); + color: var(--interactive-normal) !important; + + &:hover{ + filter: brightness(2); + } } From b721059e06efb4b727b8e6e1529cfedd79c76925 Mon Sep 17 00:00:00 2001 From: zyqunix <117040076+zyqunix@users.noreply.github.com> Date: Fri, 11 Apr 2025 03:48:44 +0200 Subject: [PATCH 017/198] Add morse code plugin (#226) * Add a morse code plugin to trans to/from morse code * comment * Update index.ts --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- src/equicordplugins/morse/index.ts | 73 ++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/equicordplugins/morse/index.ts diff --git a/src/equicordplugins/morse/index.ts b/src/equicordplugins/morse/index.ts new file mode 100644 index 00000000..1bb7d75e --- /dev/null +++ b/src/equicordplugins/morse/index.ts @@ -0,0 +1,73 @@ +/* + * 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 . +*/ + +import { ApplicationCommandInputType, ApplicationCommandOptionType } from "@api/Commands"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +const morseMap = { + A: ".-", B: "-...", C: "-.-.", D: "-..", E: ".", F: "..-.", + G: "--.", H: "....", I: "..", J: ".---", K: "-.-", L: ".-..", + M: "--", N: "-.", O: "---", P: ".--.", Q: "--.-", R: ".-.", + S: "...", T: "-", U: "..-", V: "...-", W: ".--", X: "-..-", + Y: "-.--", Z: "--..", + 0: "-----", 1: ".----", 2: "..---", 3: "...--", 4: "....-", + 5: ".....", 6: "-....", 7: "--...", 8: "---..", 9: "----.", + " ": "/" +}; + +const toMorse = (text: string) => { + return text.toUpperCase().split("").map(char => morseMap[char] ?? "").join(" "); +}; + +const fromMorse = (text: string) => { + const reversedMap = Object.fromEntries(Object.entries(morseMap).map(([k, v]) => [v, k])); + const raw = text.split(" ").map(code => reversedMap[code] ?? "").join("").toLowerCase(); + return raw.charAt(0).toUpperCase() + raw.slice(1); +}; + +// boo regex +const isMorse = (text: string) => /^[.\-/ ]+$/.test(text); + +export default definePlugin({ + name: "Morse", + description: "A slash command to translate to/from morse code.", + authors: [EquicordDevs.zyqunix], + commands: [ + { + inputType: ApplicationCommandInputType.BUILT_IN_TEXT, + name: "morse", + description: "Translate to or from Morse code", + options: [ + { + name: "text", + description: "Text to convert", + type: ApplicationCommandOptionType.STRING, + required: true + } + ], + execute: opts => { + const input = opts.find(o => o.name === "text")?.value as string; + const output = isMorse(input) ? fromMorse(input) : toMorse(input); + return { + content: `${output}` + }; + }, + } + ] +}); From cf7dc5522d785a14878662d28153a5b90507bab1 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 10 Apr 2025 21:50:51 -0400 Subject: [PATCH 018/198] Update Readme For Morse --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 99478980..39692e4f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-165 additional plugins +166 additional plugins ### All Platforms @@ -105,6 +105,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - MessageTranslate by Samwich - ModalFade by Kyuuhachi - MoreStickers by Leko & Arjix +- Morse by zyqunix - NeverPausePreviews by vappstar - NewPluginsManager by Sqaaakoi - NoAppsAllowed by kvba From 1fa6181f7e6526e1bf2e85260d9051000916c47c Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 11 Apr 2025 18:14:09 +0200 Subject: [PATCH 019/198] Translate: use newer google api with better results --- src/plugins/translate/utils.ts | 38 +++++++++++----------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/src/plugins/translate/utils.ts b/src/plugins/translate/utils.ts index aff64e8a..9bb175ad 100644 --- a/src/plugins/translate/utils.ts +++ b/src/plugins/translate/utils.ts @@ -29,11 +29,8 @@ export const cl = classNameFactory("vc-trans-"); const Native = VencordNative.pluginHelpers.Translate as PluginNative; interface GoogleData { - src: string; - sentences: { - // 🏳️‍⚧️ - trans: string; - }[]; + translation: string; + sourceLanguage: string; } interface DeeplData { @@ -77,21 +74,13 @@ export async function translate(kind: "received" | "sent", text: string): Promis } async function googleTranslate(text: string, sourceLang: string, targetLang: string): Promise { - const url = "https://translate.googleapis.com/translate_a/single?" + new URLSearchParams({ - // see https://stackoverflow.com/a/29537590 for more params - // holy shidd nvidia - client: "gtx", - // source language - sl: sourceLang, - // target language - tl: targetLang, - // what to return, t = translation probably - dt: "t", - // Send json object response instead of weird array - dj: "1", - source: "input", - // query, duh - q: text + const url = "https://translate-pa.googleapis.com/v1/translate?" + new URLSearchParams({ + "params.client": "gtx", + "dataTypes": "TRANSLATION", + "key": "AIzaSyDLEeFI5OtFBwYBIoK_jj5m32rZK5CkCXA", // some google API key + "query.sourceLanguage": sourceLang, + "query.targetLanguage": targetLang, + "query.text": text, }); const res = await fetch(url); @@ -101,14 +90,11 @@ async function googleTranslate(text: string, sourceLang: string, targetLang: str + `\n${res.status} ${res.statusText}` ); - const { src, sentences }: GoogleData = await res.json(); + const { sourceLanguage, translation }: GoogleData = await res.json(); return { - sourceLanguage: GoogleLanguages[src] ?? src, - text: sentences. - map(s => s?.trans). - filter(Boolean). - join("") + sourceLanguage: GoogleLanguages[sourceLanguage] ?? sourceLanguage, + text: translation }; } From f858a745a65ba98d36ee8b370c0a1a1aad5c9a3e Mon Sep 17 00:00:00 2001 From: mochie Date: Fri, 11 Apr 2025 23:46:52 +0200 Subject: [PATCH 020/198] add color to "replying to {user}" (#228) --- src/equicordplugins/customUserColors/index.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index ce9e66e5..caa2a03a 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -125,6 +125,13 @@ export default definePlugin({ ], predicate: () => settings.store.dmList, }, + { + find: '"Reply Chain Nudge")', + replacement: { + match: /(,color:)(\i),/, + replace: "$1$self.colorInReplyingTo(arguments[0]) ?? $2,", + }, + }, ], colorDMList(a: any): string | undefined { @@ -142,5 +149,10 @@ export default definePlugin({ const color = getCustomColorString(a.message.author.id, true); return color ?? roleColor ?? undefined; - } + }, + + colorInReplyingTo(a: any) { + const { id } = a.reply.message.author; + return getCustomColorString(id, true); + }, }); From d586b33bef96b7a53a0cf765a1e2a20e2759873c Mon Sep 17 00:00:00 2001 From: thororen <78185467+thororen1234@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:40:24 -0400 Subject: [PATCH 021/198] Patch Fixes --- src/equicordplugins/betterActivities/index.tsx | 2 +- src/plugins/accountPanelServerProfile/index.tsx | 2 +- src/plugins/roleColorEverywhere/index.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/equicordplugins/betterActivities/index.tsx b/src/equicordplugins/betterActivities/index.tsx index b87f8ff3..ec4c0f41 100644 --- a/src/equicordplugins/betterActivities/index.tsx +++ b/src/equicordplugins/betterActivities/index.tsx @@ -46,7 +46,7 @@ export default definePlugin({ // Show all activities in the user popout/sidebar find: '"UserProfilePopoutBody"', replacement: { - match: /(?<=(\i)\.id\)\}\)\),(\i).*?)\(0,.{0,100}\i\.activity\}\)/, + match: /(?<=(\i)\.id\)\}\)\),(\i).*?)\(0,.{0,100}\i\.id,onClose:\i\}\)/, replace: "$self.showAllActivitiesComponent({ activity: $2, user: $1 })" }, predicate: () => settings.store.userPopout diff --git a/src/plugins/accountPanelServerProfile/index.tsx b/src/plugins/accountPanelServerProfile/index.tsx index 2b212d34..7115ae41 100644 --- a/src/plugins/accountPanelServerProfile/index.tsx +++ b/src/plugins/accountPanelServerProfile/index.tsx @@ -77,7 +77,7 @@ export default definePlugin({ replace: "$self.useAccountPanelRef();$&" }, { - match: /(\.AVATAR,children:.+?renderPopout:(\i)=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/, + match: /(\.AVATAR,children:.+?renderPopout:\((\i),\i\)=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/, replace: (_, rest, popoutProps, originalPopout, currentUser) => `${rest}$self.UserProfile({popoutProps:${popoutProps},currentUser:${currentUser},originalRenderPopout:()=>{${originalPopout}}})` }, { diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index f16161fa..04b1ec8e 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -85,7 +85,7 @@ export default definePlugin({ find: ".USER_MENTION)", replacement: [ { - match: /(?<=onContextMenu:\i,color:)\i(?=,onClick)(?<=user:(\i),channel:(\i).+?)/, + match: /(?<=onContextMenu:\i,color:)\i(?=\},)(?<=user:(\i),channel:(\i).+?)/, replace: "$self.getColorInt($1?.id,$2?.id)", } ], From 71f00c8d528c94d1ec2101a63e3d981e15460f67 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 13 Apr 2025 13:28:53 -0400 Subject: [PATCH 022/198] Remove Wayland GUI --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 39692e4f..f0b87abb 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,6 @@ MacOS Linux - [GUI-X11](https://github.com/Equicord/Equilotl/releases/latest/download/Equilotl-x11) -- [GUI-Wayland](https://github.com/Equicord/Equilotl/releases/latest/download/Equilotl-wayland) - [CLI](https://github.com/Equicord/Equilotl/releases/latest/download/EquilotlCli-Linux) - [AUR](https://aur.archlinux.org/packages?O=0&K=equicord) From fa2fb341c8ad4dfd5708eacfec19385078f74e59 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 13 Apr 2025 21:46:55 -0400 Subject: [PATCH 023/198] Fix FriendCodes --- src/equicordplugins/friendCodes/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/friendCodes/index.tsx b/src/equicordplugins/friendCodes/index.tsx index 2b9c3300..967230a7 100644 --- a/src/equicordplugins/friendCodes/index.tsx +++ b/src/equicordplugins/friendCodes/index.tsx @@ -17,7 +17,7 @@ export default definePlugin({ { find: "#{intl::ADD_FRIEND})}),(", replacement: { - match: /\.Fragment[^]*?children:\[[^]*?}\)/, + match: /header,children:\[.*?\{\}\)/, replace: "$&,$self.FriendCodesPanel" } } From 1158f5616dc960453cfad6ceb4346acfb4b6027b Mon Sep 17 00:00:00 2001 From: Eazvy <57739965+Eazvy@users.noreply.github.com> Date: Sun, 13 Apr 2025 21:49:01 -0400 Subject: [PATCH 024/198] update (userVoiceShow) avatar-size / guild icon animated / user count (#231) * Add files via upload * Update components.tsx * update horrible written logic --- src/plugins/userVoiceShow/components.tsx | 32 ++++++++++++++++++++++-- src/plugins/userVoiceShow/index.tsx | 4 +-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/plugins/userVoiceShow/components.tsx b/src/plugins/userVoiceShow/components.tsx index 9029cdc5..675588ed 100644 --- a/src/plugins/userVoiceShow/components.tsx +++ b/src/plugins/userVoiceShow/components.tsx @@ -111,9 +111,37 @@ function VoiceChannelTooltip({ channel, isLocked }: VoiceChannelTooltipProps) { {guild.name} )} -
+
{channelIcon} - {channelName} + + {channelName} + +
+ + {users.length < 10 ? `0${users.length}` : `${users.length}`} + + + {channel.userLimit < 10 ? `0${channel.userLimit}` : `${channel.userLimit}`} + +
{isLocked ? : } diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx index 3d119c43..67e5e022 100644 --- a/src/plugins/userVoiceShow/index.tsx +++ b/src/plugins/userVoiceShow/index.tsx @@ -21,7 +21,7 @@ import "./style.css"; import { addMemberListDecorator, removeMemberListDecorator } from "@api/MemberListDecorators"; import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations"; import { definePluginSettings } from "@api/Settings"; -import { Devs } from "@utils/constants"; +import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { VoiceChannelIndicator } from "./components"; @@ -50,7 +50,7 @@ const settings = definePluginSettings({ export default definePlugin({ name: "UserVoiceShow", description: "Shows an indicator when a user is in a Voice Channel", - authors: [Devs.Nuckyz, Devs.LordElias], + authors: [Devs.Nuckyz, Devs.LordElias, EquicordDevs.omaw], dependencies: ["MemberListDecoratorsAPI", "MessageDecorationsAPI"], settings, From fe0309ffaa88fc7b47fb58885bf0b3415656ac8a Mon Sep 17 00:00:00 2001 From: iilwy Date: Mon, 14 Apr 2025 06:41:50 -0500 Subject: [PATCH 025/198] fix "Disable minimum window size" option (#3335) Fixes #3330 Fixes #878 --- src/main/patcher.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/patcher.ts b/src/main/patcher.ts index e5b87290..60b169af 100644 --- a/src/main/patcher.ts +++ b/src/main/patcher.ts @@ -87,6 +87,11 @@ if (!IS_VANILLA) { options.backgroundColor = "#00000000"; } + if (settings.disableMinSize) { + options.minWidth = 0; + options.minHeight = 0; + } + const needsVibrancy = process.platform === "darwin" && settings.macosVibrancyStyle; if (needsVibrancy) { @@ -99,6 +104,12 @@ if (!IS_VANILLA) { process.env.DISCORD_PRELOAD = original; super(options); + + if (settings.disableMinSize) { + // Disable the Electron call entirely so that Discord can't dynamically change the size + this.setMinimumSize = (width: number, height: number) => { }; + } + initIpc(this); } else super(options); } @@ -117,16 +128,9 @@ if (!IS_VANILLA) { BrowserWindow }; - // Patch appSettings to force enable devtools and optionally disable min size + // Patch appSettings to force enable devtools onceDefined(global, "appSettings", s => { s.set("DANGEROUS_ENABLE_DEVTOOLS_ONLY_ENABLE_IF_YOU_KNOW_WHAT_YOURE_DOING", true); - if (settings.disableMinSize) { - s.set("MIN_WIDTH", 0); - s.set("MIN_HEIGHT", 0); - } else { - s.set("MIN_WIDTH", 940); - s.set("MIN_HEIGHT", 500); - } }); process.env.DATA_DIR = join(app.getPath("userData"), "..", "Vencord"); From a8c01a2a05bcacbd326143bd9839cbd9e1ff1d71 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 14 Apr 2025 14:43:13 +0200 Subject: [PATCH 026/198] ConsoleShortcuts: fix module preloader --- src/plugins/consoleShortcuts/index.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index 5afdbdd9..5ddf4639 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -20,6 +20,7 @@ import { Devs } from "@utils/constants"; import { getCurrentChannel, getCurrentGuild } from "@utils/discord"; import { runtimeHashMessageKey } from "@utils/intlHash"; import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy"; +import { sleep } from "@utils/misc"; import { ModalAPI } from "@utils/modal"; import { relaunch } from "@utils/native"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; @@ -206,10 +207,13 @@ function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) { return value; } +const webpackModulesProbablyLoaded = Webpack.onceReady.then(() => sleep(1000)); + export default definePlugin({ name: "ConsoleShortcuts", description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.", authors: [Devs.Ven], + startAt: StartAt.Init, patches: [ { @@ -221,7 +225,7 @@ export default definePlugin({ } ], - startAt: StartAt.Init, + start() { const shortcuts = makeShortcuts(); window.shortcutList = {}; @@ -242,18 +246,16 @@ export default definePlugin({ } // unproxy loaded modules - Webpack.onceReady.then(() => { - setTimeout(() => this.eagerLoad(false), 1000); + this.eagerLoad(false); - if (!IS_WEB) { - const Native = VencordNative.pluginHelpers.ConsoleShortcuts as PluginNative; - Native.initDevtoolsOpenEagerLoad(); - } - }); + if (!IS_WEB) { + const Native = VencordNative.pluginHelpers.ConsoleShortcuts as PluginNative; + Native.initDevtoolsOpenEagerLoad(); + } }, async eagerLoad(forceLoad: boolean) { - await Webpack.onceReady; + await webpackModulesProbablyLoaded; const shortcuts = makeShortcuts(); From 0f4d3dfd3accc2d9f38a47fea56bebb962b27a05 Mon Sep 17 00:00:00 2001 From: Vending Machine Date: Mon, 14 Apr 2025 15:21:30 +0200 Subject: [PATCH 027/198] SpotifyControls: fix SeekBar not updating (#3381) Also slightly reworks LazyComponents for more useful typing --- src/components/ErrorBoundary.tsx | 6 ++--- src/debug/runReporter.ts | 2 +- src/plugins/betterSettings/index.tsx | 3 +-- src/plugins/consoleShortcuts/index.ts | 4 +-- .../spotifyControls/PlayerComponent.tsx | 24 ++++++++++-------- src/plugins/spotifyControls/SeekBar.ts | 25 +++++++++++++++++++ src/utils/lazyReact.tsx | 10 +++++--- src/webpack/common/internal.tsx | 6 ++--- src/webpack/common/types/components.d.ts | 4 +-- 9 files changed, 57 insertions(+), 27 deletions(-) create mode 100644 src/plugins/spotifyControls/SeekBar.ts diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index bb2df342..e609d564 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -18,7 +18,7 @@ import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import { LazyComponent } from "@utils/react"; +import { LazyComponent, LazyComponentWrapper } from "@utils/react"; import { React } from "@webpack/common"; import { ErrorCard } from "./ErrorCard"; @@ -107,9 +107,9 @@ const ErrorBoundary = LazyComponent(() => { } }; }) as - React.ComponentType> & { + LazyComponentWrapper> & { wrap(Component: React.ComponentType, errorBoundaryProps?: Omit, "wrappedProps">): React.FunctionComponent; - }; + }>; ErrorBoundary.wrap = (Component, errorBoundaryProps) => props => ( diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 2ca83b7f..21802b6a 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -87,7 +87,7 @@ async function runReporter() { result = Webpack[method](...args); } - if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail"); + if (result == null || (result.$$vencordGetWrappedComponent != null && result.$$vencordGetWrappedComponent() == null)) throw new Error("Webpack Find Fail"); } catch (e) { let logMessage = searchType; if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index 84e338ef..cbf94c2b 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -142,8 +142,7 @@ export default definePlugin({ // Thus, we sanity check webpack modules Layer(props: LayerProps) { try { - // @ts-ignore - [FocusLock.$$vencordInternal(), ComponentDispatch, Classes].forEach(e => e.test); + [FocusLock.$$vencordGetWrappedComponent(), ComponentDispatch, Classes].forEach(e => e.test); } catch { new Logger("BetterSettings").error("Failed to find some components"); return props.children; diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index 5ddf4639..49e67158 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -174,8 +174,8 @@ function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) { function unwrapProxy(value: any) { if (value[SYM_LAZY_GET]) { forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED]; - } else if (value.$$vencordInternal) { - return forceLoad ? value.$$vencordInternal() : value; + } else if (value.$$vencordGetWrappedComponent) { + return forceLoad ? value.$$vencordGetWrappedComponent() : value; } return value; diff --git a/src/plugins/spotifyControls/PlayerComponent.tsx b/src/plugins/spotifyControls/PlayerComponent.tsx index 4184931f..78a69a14 100644 --- a/src/plugins/spotifyControls/PlayerComponent.tsx +++ b/src/plugins/spotifyControls/PlayerComponent.tsx @@ -28,6 +28,7 @@ import { openImageModal } from "@utils/discord"; import { classes, copyWithToast } from "@utils/misc"; import { ContextMenuApi, FluxDispatcher, Forms, Menu, React, useEffect, useState, useStateFromStores } from "@webpack/common"; +import { SeekBar } from "./SeekBar"; import { SpotifyStore, Track } from "./SpotifyStore"; const cl = classNameFactory("vc-spotify-"); @@ -160,7 +161,7 @@ const seek = debounce((v: number) => { SpotifyStore.seek(v); }); -function SeekBar() { +function SpotifySeekBar() { const { duration } = SpotifyStore.track!; const [storePosition, isSettingPosition, isPlaying] = useStateFromStores( @@ -181,6 +182,12 @@ function SeekBar() { } }, [storePosition, isSettingPosition, isPlaying]); + const onChange = (v: number) => { + if (isSettingPosition) return; + setPosition(v); + seek(v); + }; + return (
{msToHuman(position)} - { - if (isSettingPosition) return; - setPosition(v); - seek(v); - }} - renderValue={msToHuman} + onValueChange={onChange} + asValueChanges={onChange} + onValueRender={msToHuman} /> - +
); diff --git a/src/plugins/spotifyControls/SeekBar.ts b/src/plugins/spotifyControls/SeekBar.ts new file mode 100644 index 00000000..8d6c8a30 --- /dev/null +++ b/src/plugins/spotifyControls/SeekBar.ts @@ -0,0 +1,25 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { LazyComponent } from "@utils/lazyReact"; +import { Slider } from "@webpack/common"; + +export const SeekBar = LazyComponent(() => { + const SliderClass = Slider.$$vencordGetWrappedComponent(); + + // Discord's Slider does not update `state.value` when `props.initialValue` changes if state.value is not nullish. + // We extend their class and override their `getDerivedStateFromProps` to update the value + return class SeekBar extends SliderClass { + static getDerivedStateFromProps(props: any, state: any) { + const newState = super.getDerivedStateFromProps!(props, state); + if (newState) { + newState.value = props.initialValue; + } + + return newState; + } + }; +}); diff --git a/src/utils/lazyReact.tsx b/src/utils/lazyReact.tsx index 4896a058..0a15bf92 100644 --- a/src/utils/lazyReact.tsx +++ b/src/utils/lazyReact.tsx @@ -4,26 +4,28 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { ComponentType } from "react"; +import type { ComponentType } from "react"; import { makeLazy } from "./lazy"; const NoopComponent = () => null; +export type LazyComponentWrapper = ComponentType & { $$vencordGetWrappedComponent(): ComponentType; }; + /** * A lazy component. The factory method is called on first render. * @param factory Function returning a Component * @param attempts How many times to try to get the component before giving up * @returns Result of factory function */ -export function LazyComponent(factory: () => React.ComponentType, attempts = 5) { +export function LazyComponent(factory: () => ComponentType, attempts = 5): LazyComponentWrapper> { const get = makeLazy(factory, attempts); const LazyComponent = (props: T) => { const Component = get() ?? NoopComponent; return ; }; - LazyComponent.$$vencordInternal = get; + LazyComponent.$$vencordGetWrappedComponent = get; - return LazyComponent as ComponentType; + return LazyComponent; } diff --git a/src/webpack/common/internal.tsx b/src/webpack/common/internal.tsx index 8957c254..090d9898 100644 --- a/src/webpack/common/internal.tsx +++ b/src/webpack/common/internal.tsx @@ -16,19 +16,19 @@ * along with this program. If not, see . */ -import { LazyComponent } from "@utils/react"; +import { LazyComponent, LazyComponentWrapper } from "@utils/react"; // eslint-disable-next-line path-alias/no-relative import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack"; -export function waitForComponent = React.ComponentType & Record>(name: string, filter: FilterFn | string | string[]): T { +export function waitForComponent = React.ComponentType & Record>(name: string, filter: FilterFn | string | string[]) { if (IS_REPORTER) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]); let myValue: T = function () { throw new Error(`Vencord could not find the ${name} Component`); } as any; - const lazyComponent = LazyComponent(() => myValue) as T; + const lazyComponent = LazyComponent(() => myValue) as LazyComponentWrapper; waitFor(filter, (v: any) => { myValue = v; Object.assign(lazyComponent, v); diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index 2b8ee92a..b5f4ff5c 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import type { ComponentPropsWithRef, ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, JSX, KeyboardEvent, MouseEvent, PointerEvent, PropsWithChildren, ReactNode, Ref } from "react"; +import type { ComponentClass, ComponentPropsWithRef, ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, JSX, KeyboardEvent, MouseEvent, PointerEvent, PropsWithChildren, ReactNode, Ref } from "react"; export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code"; @@ -356,7 +356,7 @@ export type SearchableSelect = ComponentType>; -export type Slider = ComponentType Date: Mon, 14 Apr 2025 12:42:35 -0400 Subject: [PATCH 028/198] NixOS Building --- .github/workflows/build.yml | 9 ---- .github/workflows/nixosBuild.yml | 71 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/nixosBuild.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a1c8ee8..52cedfbf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,15 +66,6 @@ jobs: rm release/package.json rm release/*.map - - name: get-npm-version - id: package-version - uses: martinbeentjes/npm-get-version-action@v1.3.1 - - - name: Upload Equicord Tagged - if: startsWith(github.ref, 'refs/tags/') - run: | - gh release upload v${{ steps.package-version.outputs.current-version}} --clobber dist/release/* - - name: Upload Equicord Stable if: ${{ github.ref_name == 'main' }} run: | diff --git a/.github/workflows/nixosBuild.yml b/.github/workflows/nixosBuild.yml new file mode 100644 index 00000000..02f990e1 --- /dev/null +++ b/.github/workflows/nixosBuild.yml @@ -0,0 +1,71 @@ +name: NixOS Build +on: + schedule: + - cron: 0 0 * * * +env: + FORCE_COLOR: true + GITHUB_TOKEN: ${{ secrets.ETOKEN }} + +permissions: write-all + +jobs: + Build: + name: Build Equicord + runs-on: self-hosted + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v3 + + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --no-frozen-lockfile + + - name: Build web + run: pnpm buildWebStandalone + + - name: Build + run: pnpm buildStandalone + + - name: Generate plugin list + run: pnpm generatePluginJson dist/vencordplugins.json + + - name: Generate Equicord plugin list + run: pnpm generateEquicordPluginJson dist/equicordplugins.json + + - name: Collect files to be released + run: | + cd dist + mkdir release + + cp browser/browser.* release + cp Vencord.user.{js,js.LEGAL.txt} release + + # copy the plugin data jsons, the extension zips and the desktop/vesktop asars + cp *.{json,zip,asar} release + + # legacy un-asared files + cp desktop/* release + for file in equibop/*; do + filename=$(basename "$file") + cp "$file" "release/equibop${filename^}" + done + + find release -size 0 -delete + rm release/package.json + rm release/*.map + + - name: Get current date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%d')" + + - name: Upload Equicord Stable + run: | + gh release create ${{ steps.date.outputs.date }} --latest=false + gh release upload ${{ steps.date.outputs.date }} --clobber dist/release/* From 82f9cd1d3aa3138bf8a06f913eac511c61ec7be5 Mon Sep 17 00:00:00 2001 From: Elvyra <88881326+EepyElvyra@users.noreply.github.com> Date: Tue, 15 Apr 2025 01:43:50 +0200 Subject: [PATCH 029/198] Fix: RoleColorEverywhere, AccountPanelServerProfile, BANger (#3378) Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Co-authored-by: Vending Machine --- src/plugins/accountPanelServerProfile/index.tsx | 2 +- src/plugins/banger/index.ts | 2 +- src/plugins/roleColorEverywhere/index.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/accountPanelServerProfile/index.tsx b/src/plugins/accountPanelServerProfile/index.tsx index 2b212d34..8b561581 100644 --- a/src/plugins/accountPanelServerProfile/index.tsx +++ b/src/plugins/accountPanelServerProfile/index.tsx @@ -77,7 +77,7 @@ export default definePlugin({ replace: "$self.useAccountPanelRef();$&" }, { - match: /(\.AVATAR,children:.+?renderPopout:(\i)=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/, + match: /(\.AVATAR,children:.+?renderPopout:(\(\i,\i\))=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/, replace: (_, rest, popoutProps, originalPopout, currentUser) => `${rest}$self.UserProfile({popoutProps:${popoutProps},currentUser:${currentUser},originalRenderPopout:()=>{${originalPopout}}})` }, { diff --git a/src/plugins/banger/index.ts b/src/plugins/banger/index.ts index f13fd351..eed0e1b4 100644 --- a/src/plugins/banger/index.ts +++ b/src/plugins/banger/index.ts @@ -36,7 +36,7 @@ export default definePlugin({ settings, patches: [ { - find: "#{intl::BAN_CONFIRM_TITLE}", + find: "#{intl::jeKpoq::raw}", // BAN_CONFIRM_TITLE replacement: { match: /src:\i\("?\d+"?\)/g, replace: "src:$self.source" diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index b81a0cce..71f87b13 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -84,8 +84,8 @@ export default definePlugin({ find: ".USER_MENTION)", replacement: [ { - match: /(?<=onContextMenu:\i,color:)\i(?=,onClick)(?<=user:(\i),channel:(\i).+?)/, - replace: "$self.getColorInt($1?.id,$2?.id)", + match: /(?<=onContextMenu:\i,color:)\i(?<=\.getNickname\((\i),\i,(\i).+?)/, + replace: "$self.getColorInt($2?.id,$1)", } ], predicate: () => settings.store.chatMentions From 9d2da2a87e2cb557f6298c53d051ac70bb46c264 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:05:42 -0400 Subject: [PATCH 030/198] Canary Fixes --- src/equicordplugins/betterActivities/index.tsx | 2 +- src/equicordplugins/betterBlockedUsers/index.tsx | 2 +- src/equicordplugins/friendCodes/index.tsx | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/equicordplugins/betterActivities/index.tsx b/src/equicordplugins/betterActivities/index.tsx index ec4c0f41..a797b7e1 100644 --- a/src/equicordplugins/betterActivities/index.tsx +++ b/src/equicordplugins/betterActivities/index.tsx @@ -44,7 +44,7 @@ export default definePlugin({ }, { // Show all activities in the user popout/sidebar - find: '"UserProfilePopoutBody"', + find: "hasAvatarForGuild(null", replacement: { match: /(?<=(\i)\.id\)\}\)\),(\i).*?)\(0,.{0,100}\i\.id,onClose:\i\}\)/, replace: "$self.showAllActivitiesComponent({ activity: $2, user: $1 })" diff --git a/src/equicordplugins/betterBlockedUsers/index.tsx b/src/equicordplugins/betterBlockedUsers/index.tsx index 258933d2..b3d64864 100644 --- a/src/equicordplugins/betterBlockedUsers/index.tsx +++ b/src/equicordplugins/betterBlockedUsers/index.tsx @@ -83,7 +83,7 @@ export default definePlugin({ ] }, { - find: "UserProfileModalHeaderActionButtons", + find: "#{intl::OUTGOING_FRIEND_REQUEST}", replacement: [ { match: /(?<=return \i)\|\|(\i)===.*?.FRIEND/, diff --git a/src/equicordplugins/friendCodes/index.tsx b/src/equicordplugins/friendCodes/index.tsx index 967230a7..b2d74c22 100644 --- a/src/equicordplugins/friendCodes/index.tsx +++ b/src/equicordplugins/friendCodes/index.tsx @@ -19,7 +19,8 @@ export default definePlugin({ replacement: { match: /header,children:\[.*?\{\}\)/, replace: "$&,$self.FriendCodesPanel" - } + }, + noWarn: true, } ], From 864457d98329e84bbbc8fcbaa4adfae50e72af20 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:21:35 -0400 Subject: [PATCH 031/198] Revert "Canary Fixes" This reverts commit 9d2da2a87e2cb557f6298c53d051ac70bb46c264. --- src/equicordplugins/betterActivities/index.tsx | 2 +- src/equicordplugins/betterBlockedUsers/index.tsx | 2 +- src/equicordplugins/friendCodes/index.tsx | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/equicordplugins/betterActivities/index.tsx b/src/equicordplugins/betterActivities/index.tsx index a797b7e1..ec4c0f41 100644 --- a/src/equicordplugins/betterActivities/index.tsx +++ b/src/equicordplugins/betterActivities/index.tsx @@ -44,7 +44,7 @@ export default definePlugin({ }, { // Show all activities in the user popout/sidebar - find: "hasAvatarForGuild(null", + find: '"UserProfilePopoutBody"', replacement: { match: /(?<=(\i)\.id\)\}\)\),(\i).*?)\(0,.{0,100}\i\.id,onClose:\i\}\)/, replace: "$self.showAllActivitiesComponent({ activity: $2, user: $1 })" diff --git a/src/equicordplugins/betterBlockedUsers/index.tsx b/src/equicordplugins/betterBlockedUsers/index.tsx index b3d64864..258933d2 100644 --- a/src/equicordplugins/betterBlockedUsers/index.tsx +++ b/src/equicordplugins/betterBlockedUsers/index.tsx @@ -83,7 +83,7 @@ export default definePlugin({ ] }, { - find: "#{intl::OUTGOING_FRIEND_REQUEST}", + find: "UserProfileModalHeaderActionButtons", replacement: [ { match: /(?<=return \i)\|\|(\i)===.*?.FRIEND/, diff --git a/src/equicordplugins/friendCodes/index.tsx b/src/equicordplugins/friendCodes/index.tsx index b2d74c22..967230a7 100644 --- a/src/equicordplugins/friendCodes/index.tsx +++ b/src/equicordplugins/friendCodes/index.tsx @@ -19,8 +19,7 @@ export default definePlugin({ replacement: { match: /header,children:\[.*?\{\}\)/, replace: "$&,$self.FriendCodesPanel" - }, - noWarn: true, + } } ], From 5fe2dc34bfb6c933b3db54b08bf4f0ea8e6a18a7 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:22:44 -0400 Subject: [PATCH 032/198] Fix Lint --- src/equicordplugins/loginWithQR/ui/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/loginWithQR/ui/index.ts b/src/equicordplugins/loginWithQR/ui/index.ts index 5400ae2d..1277db7a 100644 --- a/src/equicordplugins/loginWithQR/ui/index.ts +++ b/src/equicordplugins/loginWithQR/ui/index.ts @@ -30,7 +30,7 @@ type Spinner = ComponentType, "children"> & }; // https://github.com/Kyuuhachi/VencordPlugins/blob/main/MessageLinkTooltip/index.tsx#L11-L33 -export const Spinner = findComponentByCodeLazy('"pulsingEllipsis"') as Spinner; +export const Spinner = findComponentByCodeLazy('"pulsingEllipsis"') as unknown as Spinner; export const QrCodeIcon = findComponentByCodeLazy("0v3ZM20"); From bafbc7960158ed642e57616132dd5413b845af2d Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:23:07 -0400 Subject: [PATCH 033/198] Reapply "Canary Fixes" This reverts commit 864457d98329e84bbbc8fcbaa4adfae50e72af20. --- src/equicordplugins/betterActivities/index.tsx | 2 +- src/equicordplugins/betterBlockedUsers/index.tsx | 2 +- src/equicordplugins/friendCodes/index.tsx | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/equicordplugins/betterActivities/index.tsx b/src/equicordplugins/betterActivities/index.tsx index ec4c0f41..a797b7e1 100644 --- a/src/equicordplugins/betterActivities/index.tsx +++ b/src/equicordplugins/betterActivities/index.tsx @@ -44,7 +44,7 @@ export default definePlugin({ }, { // Show all activities in the user popout/sidebar - find: '"UserProfilePopoutBody"', + find: "hasAvatarForGuild(null", replacement: { match: /(?<=(\i)\.id\)\}\)\),(\i).*?)\(0,.{0,100}\i\.id,onClose:\i\}\)/, replace: "$self.showAllActivitiesComponent({ activity: $2, user: $1 })" diff --git a/src/equicordplugins/betterBlockedUsers/index.tsx b/src/equicordplugins/betterBlockedUsers/index.tsx index 258933d2..b3d64864 100644 --- a/src/equicordplugins/betterBlockedUsers/index.tsx +++ b/src/equicordplugins/betterBlockedUsers/index.tsx @@ -83,7 +83,7 @@ export default definePlugin({ ] }, { - find: "UserProfileModalHeaderActionButtons", + find: "#{intl::OUTGOING_FRIEND_REQUEST}", replacement: [ { match: /(?<=return \i)\|\|(\i)===.*?.FRIEND/, diff --git a/src/equicordplugins/friendCodes/index.tsx b/src/equicordplugins/friendCodes/index.tsx index 967230a7..b2d74c22 100644 --- a/src/equicordplugins/friendCodes/index.tsx +++ b/src/equicordplugins/friendCodes/index.tsx @@ -19,7 +19,8 @@ export default definePlugin({ replacement: { match: /header,children:\[.*?\{\}\)/, replace: "$&,$self.FriendCodesPanel" - } + }, + noWarn: true, } ], From 5d2b25d8513eba8ee1c6015880e20a542eba2173 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:25:23 -0400 Subject: [PATCH 034/198] Fix Lint Again --- src/webpack/common/internal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/webpack/common/internal.tsx b/src/webpack/common/internal.tsx index 090d9898..aee86f6d 100644 --- a/src/webpack/common/internal.tsx +++ b/src/webpack/common/internal.tsx @@ -18,7 +18,6 @@ import { LazyComponent, LazyComponentWrapper } from "@utils/react"; -// eslint-disable-next-line path-alias/no-relative import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack"; export function waitForComponent = React.ComponentType & Record>(name: string, filter: FilterFn | string | string[]) { From 5fb7ad2ad09cb25258d9045e9c779392fabba367 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 16 Apr 2025 10:08:14 -0400 Subject: [PATCH 035/198] Canary Fixes --- src/equicordplugins/bannersEverywhere/index.tsx | 6 +++--- src/equicordplugins/betterActivities/index.tsx | 2 +- src/equicordplugins/clientSideBlock/index.tsx | 2 +- src/equicordplugins/customTimestamps/index.tsx | 4 ++-- src/equicordplugins/customUserColors/index.tsx | 2 +- src/plugins/_api/memberListDecorators/index.tsx | 4 ++-- src/plugins/ircColors/index.ts | 4 ++-- src/webpack/common/utils.ts | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/equicordplugins/bannersEverywhere/index.tsx b/src/equicordplugins/bannersEverywhere/index.tsx index ad804c89..ab3e8da0 100644 --- a/src/equicordplugins/bannersEverywhere/index.tsx +++ b/src/equicordplugins/bannersEverywhere/index.tsx @@ -58,8 +58,8 @@ export default definePlugin({ replacement: [ { // We add the banner as a property while we can still access the user id - match: /(?<=nameplate:(\i).*?)verified:(\i).isVerifiedBot.*?name:null.*?(?=avatar:)/, - replace: "$&banner:$self.memberListBannerHook($2, $1),", + match: /verified:(\i).isVerifiedBot.*?nameplate:(\i).*?name:null.*?(?=avatar:)/, + replace: "$&banner:$self.memberListBannerHook($1, $2),", }, { match: /(?<=\),nameplate:)(\i)/, @@ -112,7 +112,7 @@ export default definePlugin({ } return ( - + ); }, diff --git a/src/equicordplugins/betterActivities/index.tsx b/src/equicordplugins/betterActivities/index.tsx index a797b7e1..6212eb33 100644 --- a/src/equicordplugins/betterActivities/index.tsx +++ b/src/equicordplugins/betterActivities/index.tsx @@ -35,7 +35,7 @@ export default definePlugin({ patches: [ { // Patch activity icons - find: '"activity-status-web"', + find: "isBlockedOrIgnored(null", replacement: { match: /(?<=hideTooltip:.{0,4}}=(\i).*?{}\))\]/, replace: ",$self.patchActivityList($1)]" diff --git a/src/equicordplugins/clientSideBlock/index.tsx b/src/equicordplugins/clientSideBlock/index.tsx index 206a4cf4..279ef958 100644 --- a/src/equicordplugins/clientSideBlock/index.tsx +++ b/src/equicordplugins/clientSideBlock/index.tsx @@ -152,7 +152,7 @@ export default definePlugin({ { find: "._areActivitiesExperimentallyHidden=(", replacement: { - match: /(?<=user:(\i),guildId:\i,channel:(\i).*?)BOOST_GEM_ICON\}\}\)\)\};/, + match: /(?<=user:(\i),guildId:\i,channel:(\i).*?)BOOST_GEM_ICON.{0,10}\);/, replace: "$&if($self.shouldHideUser($1.id, $2.id)) return null; " } }, diff --git a/src/equicordplugins/customTimestamps/index.tsx b/src/equicordplugins/customTimestamps/index.tsx index 87930f6a..d90defe3 100644 --- a/src/equicordplugins/customTimestamps/index.tsx +++ b/src/equicordplugins/customTimestamps/index.tsx @@ -71,11 +71,11 @@ export default definePlugin({ find: "#{intl::MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL}", replacement: [ { - match: /(?<=\i=null!=\i\?).{0,25}\((\i),"LT"\):\(0,\i\.\i\)\(\i,!0\)/, + match: /(?<=null!=\i\?).{0,25}\((\i),"LT"\):\(0,\i\.\i\)\(\i,!0\)/, replace: '$self.format($1,"compactFormat","[calendar]"):$self.format($1,"cozyFormat","LT")', }, { - match: /(?<=text:)\(0,\i.\i\)\((\i),"LLLL"\)(?=,)/, + match: /(?<=text:)\(\)=>\(0,\i.\i\)\((\i),"LLLL"\)(?=,)/, replace: '$self.format($1,"tooltipFormat","LLLL")', }, ] diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index caa2a03a..b75d8659 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -112,7 +112,7 @@ export default definePlugin({ predicate: () => settings.store.dmList, }, { - find: "!1,wrapContent", + find: '"AvatarWithText"', replacement: [ { match: /(\}=\i)/, diff --git a/src/plugins/_api/memberListDecorators/index.tsx b/src/plugins/_api/memberListDecorators/index.tsx index 39c82a1e..8bc80cb4 100644 --- a/src/plugins/_api/memberListDecorators/index.tsx +++ b/src/plugins/_api/memberListDecorators/index.tsx @@ -36,8 +36,8 @@ export default definePlugin({ match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/, replace: "$&vencordProps=$1," }, { - match: /#{intl::GUILD_OWNER}(?=.+?decorators:(\i)\(\)).+?\1=?\(\)=>.+?children:\[/, - replace: "$&(typeof vencordProps=='undefined'?null:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps))," + match: /(?<=children:\[)(?=.{0,300},lostPermissionTooltipText:)/, + replace: "(typeof vencordProps=='undefined'?null:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps))," } ] }, diff --git a/src/plugins/ircColors/index.ts b/src/plugins/ircColors/index.ts index 3d531459..8f8891e9 100644 --- a/src/plugins/ircColors/index.ts +++ b/src/plugins/ircColors/index.ts @@ -74,8 +74,8 @@ export default definePlugin({ { find: "#{intl::GUILD_OWNER}),children:", replacement: { - match: /(typingIndicatorRef:.+?},)(\i=.+?)color:null!=.{0,50}?(?=,)/, - replace: (_, rest1, rest2) => `${rest1}ircColor=$self.calculateNameColorForListContext(arguments[0]),${rest2}color:ircColor` + match: /(?<=color:)null!=\i\?\i:void 0/, + replace: (_, rest1, rest2) => "$self.calculateNameColorForListContext(arguments[0])" }, predicate: () => settings.store.memberListColors } diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index a2f846bb..2bf97e62 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -151,8 +151,8 @@ export const ApplicationAssetUtils = mapMangledModuleLazy("getAssetImage: size m getAssets: filters.byCode(".assets") }); -export const Clipboard: t.Clipboard = mapMangledModuleLazy('queryCommandEnabled("copy")', { - copy: filters.byCode(".copy("), +export const Clipboard: t.Clipboard = mapMangledModuleLazy("await window.navigator.clipboard.writeText(", { + copy: filters.byCode(".writeText("), SUPPORTS_COPY: e => typeof e === "boolean" }); From 524202e49dfd2efba822370a9f7cbe716eea9561 Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Wed, 16 Apr 2025 14:34:16 -0400 Subject: [PATCH 036/198] Fix clipboard api, IrcColors, and Memberlist Decorators (#3383) Co-authored-by: Vending Machine --- .../VencordSettings/PatchHelperTab.tsx | 7 +++-- .../_api/memberListDecorators/index.tsx | 7 +++-- src/plugins/betterRoleContext/index.tsx | 5 ++-- src/plugins/betterRoleDot/index.ts | 13 ++------- src/plugins/copyUserURLs/index.tsx | 5 ++-- .../ui/components/DecorationContextMenu.tsx | 5 ++-- src/plugins/ircColors/index.ts | 4 +-- .../components/RolesAndUsersPermissions.tsx | 7 +++-- .../components/ButtonRow.tsx | 29 +++++++------------ .../hooks/useCopyCooldown.ts | 5 ++-- .../previewExample.tsx | 2 +- src/plugins/webContextMenus.web/index.ts | 9 +++--- src/utils/clipboard.ts | 9 ++++++ src/utils/index.ts | 1 + src/utils/misc.ts | 11 +++---- src/webpack/common/utils.ts | 5 ---- 16 files changed, 58 insertions(+), 66 deletions(-) create mode 100644 src/utils/clipboard.ts diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index f930a40d..55822069 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -18,12 +18,13 @@ import { CodeBlock } from "@components/CodeBlock"; import { debounce } from "@shared/debounce"; +import { copyToClipboard } from "@utils/clipboard"; import { Margins } from "@utils/margins"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import { makeCodeblock } from "@utils/text"; import { Patch, ReplaceFn } from "@utils/types"; import { search } from "@webpack"; -import { Button, Clipboard, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common"; +import { Button, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common"; import { SettingsTab, wrapTab } from "./shared"; @@ -381,8 +382,8 @@ function PatchHelper() { <> Code - - + + )} diff --git a/src/plugins/_api/memberListDecorators/index.tsx b/src/plugins/_api/memberListDecorators/index.tsx index 39c82a1e..365c21f3 100644 --- a/src/plugins/_api/memberListDecorators/index.tsx +++ b/src/plugins/_api/memberListDecorators/index.tsx @@ -35,9 +35,10 @@ export default definePlugin({ { match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/, replace: "$&vencordProps=$1," - }, { - match: /#{intl::GUILD_OWNER}(?=.+?decorators:(\i)\(\)).+?\1=?\(\)=>.+?children:\[/, - replace: "$&(typeof vencordProps=='undefined'?null:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps))," + }, + { + match: /children:\[(?=.{0,300},lostPermissionTooltipText:)/, + replace: "children:[(typeof vencordProps!=='undefined'&&Vencord.Api.MemberListDecorators.__getDecorators(vencordProps))," } ] }, diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index afef6390..a9bab1a7 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -7,11 +7,12 @@ import { definePluginSettings } from "@api/Settings"; import { getUserSettingLazy } from "@api/UserSettings"; import { ImageIcon } from "@components/Icons"; +import { copyToClipboard } from "@utils/clipboard"; import { Devs } from "@utils/constants"; import { getCurrentGuild, openImageModal } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common"; +import { GuildStore, Menu, PermissionStore } from "@webpack/common"; const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); @@ -87,7 +88,7 @@ export default definePlugin({ Clipboard.copy(role.colorString!)} + action={() => copyToClipboard(role.colorString!)} icon={AppearanceIcon} /> ); diff --git a/src/plugins/betterRoleDot/index.ts b/src/plugins/betterRoleDot/index.ts index 3a8a1456..bdafe02c 100644 --- a/src/plugins/betterRoleDot/index.ts +++ b/src/plugins/betterRoleDot/index.ts @@ -18,8 +18,8 @@ import { Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; +import { copyWithToast } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { Clipboard, Toasts } from "@webpack/common"; export default definePlugin({ name: "BetterRoleDot", @@ -84,15 +84,6 @@ export default definePlugin({ }, copyToClipBoard(color: string) { - Clipboard.copy(color); - Toasts.show({ - message: "Copied to Clipboard!", - type: Toasts.Type.SUCCESS, - id: Toasts.genId(), - options: { - duration: 1000, - position: Toasts.Position.BOTTOM - } - }); + copyWithToast(color); }, }); diff --git a/src/plugins/copyUserURLs/index.tsx b/src/plugins/copyUserURLs/index.tsx index 7af8502d..9e15cc82 100644 --- a/src/plugins/copyUserURLs/index.tsx +++ b/src/plugins/copyUserURLs/index.tsx @@ -18,9 +18,10 @@ import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { LinkIcon } from "@components/Icons"; +import { copyToClipboard } from "@utils/clipboard"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { Clipboard, Menu } from "@webpack/common"; +import { Menu } from "@webpack/common"; import type { Channel, User } from "discord-types/general"; interface UserContextProps { @@ -36,7 +37,7 @@ const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: U Clipboard.copy(``)} + action={() => copyToClipboard(``)} icon={LinkIcon} /> ); diff --git a/src/plugins/decor/ui/components/DecorationContextMenu.tsx b/src/plugins/decor/ui/components/DecorationContextMenu.tsx index 7c1542f6..db3f0090 100644 --- a/src/plugins/decor/ui/components/DecorationContextMenu.tsx +++ b/src/plugins/decor/ui/components/DecorationContextMenu.tsx @@ -5,7 +5,8 @@ */ import { CopyIcon, DeleteIcon } from "@components/Icons"; -import { Alerts, Clipboard, ContextMenuApi, Menu, UserStore } from "@webpack/common"; +import { copyToClipboard } from "@utils/clipboard"; +import { Alerts, ContextMenuApi, Menu, UserStore } from "@webpack/common"; import { Decoration } from "../../lib/api"; import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore"; @@ -23,7 +24,7 @@ export default function DecorationContextMenu({ decoration }: { decoration: Deco id={cl("decoration-context-menu-copy-hash")} label="Copy Decoration Hash" icon={CopyIcon} - action={() => Clipboard.copy(decoration.hash)} + action={() => copyToClipboard(decoration.hash)} /> {decoration.authorId === UserStore.getCurrentUser().id && `${rest1}ircColor=$self.calculateNameColorForListContext(arguments[0]),${rest2}color:ircColor` + match: /(?<=roleName:\i,)color:/, + replace: "color:$self.calculateNameColorForListContext(arguments[0]),originalColor:" }, predicate: () => settings.store.memberListColors } diff --git a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx index 02662fe9..ed620d7f 100644 --- a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx +++ b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx @@ -19,10 +19,11 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { InfoIcon, OwnerCrownIcon } from "@components/Icons"; +import { copyToClipboard } from "@utils/clipboard"; import { getIntlMessage, getUniqueUsername } from "@utils/discord"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { findByCodeLazy } from "@webpack"; -import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, useMemo, UserStore, useState, useStateFromStores } from "@webpack/common"; +import { ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, useMemo, UserStore, useState, useStateFromStores } from "@webpack/common"; import { UnicodeEmoji } from "@webpack/types"; import type { Guild, Role, User } from "discord-types/general"; @@ -228,7 +229,7 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str id={cl("copy-role-id")} label={getIntlMessage("COPY_ID_ROLE")} action={() => { - Clipboard.copy(roleId); + copyToClipboard(roleId); }} /> @@ -269,7 +270,7 @@ function UserContextMenu({ userId }: { userId: string; }) { id={cl("copy-user-id")} label={getIntlMessage("COPY_ID_USER")} action={() => { - Clipboard.copy(userId); + copyToClipboard(userId); }} /> diff --git a/src/plugins/shikiCodeblocks.desktop/components/ButtonRow.tsx b/src/plugins/shikiCodeblocks.desktop/components/ButtonRow.tsx index 6f0690d9..408de154 100644 --- a/src/plugins/shikiCodeblocks.desktop/components/ButtonRow.tsx +++ b/src/plugins/shikiCodeblocks.desktop/components/ButtonRow.tsx @@ -16,9 +16,6 @@ * along with this program. If not, see . */ -import { Clipboard } from "@webpack/common"; -import { JSX } from "react"; - import { cl } from "../utils/misc"; import { CopyButton } from "./CopyButton"; @@ -28,20 +25,14 @@ export interface ButtonRowProps { } export function ButtonRow({ content, theme }: ButtonRowProps) { - const buttons: JSX.Element[] = []; - - if (Clipboard.SUPPORTS_COPY) { - buttons.push( - - ); - } - - return
{buttons}
; + return
+ +
; } diff --git a/src/plugins/shikiCodeblocks.desktop/hooks/useCopyCooldown.ts b/src/plugins/shikiCodeblocks.desktop/hooks/useCopyCooldown.ts index 414500bd..d3f35fb2 100644 --- a/src/plugins/shikiCodeblocks.desktop/hooks/useCopyCooldown.ts +++ b/src/plugins/shikiCodeblocks.desktop/hooks/useCopyCooldown.ts @@ -16,13 +16,14 @@ * along with this program. If not, see . */ -import { Clipboard, React } from "@webpack/common"; +import { copyToClipboard } from "@utils/clipboard"; +import { React } from "@webpack/common"; export function useCopyCooldown(cooldown: number) { const [copyCooldown, setCopyCooldown] = React.useState(false); function copy(text: string) { - Clipboard.copy(text); + copyToClipboard(text); setCopyCooldown(true); setTimeout(() => { diff --git a/src/plugins/shikiCodeblocks.desktop/previewExample.tsx b/src/plugins/shikiCodeblocks.desktop/previewExample.tsx index 508153b4..db7edcf0 100644 --- a/src/plugins/shikiCodeblocks.desktop/previewExample.tsx +++ b/src/plugins/shikiCodeblocks.desktop/previewExample.tsx @@ -2,7 +2,7 @@ import React from "react"; const handleClick = async () => - console.log((await import("@webpack/common")).Clipboard.copy("\u200b")); + console.log((await import("@utils/clipboard")).copyToClipboard("\u200b")); export const Example: React.FC<{ real: boolean, diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index 07eb4a3e..a0f84dd6 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -17,11 +17,12 @@ */ import { definePluginSettings } from "@api/Settings"; +import { copyToClipboard } from "@utils/clipboard"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { saveFile } from "@utils/web"; import { filters, mapMangledModuleLazy } from "@webpack"; -import { Clipboard, ComponentDispatch } from "@webpack/common"; +import { ComponentDispatch } from "@webpack/common"; const ctxMenuCallbacks = mapMangledModuleLazy('.tagName)==="TEXTAREA"||', { contextMenuCallbackWeb: filters.byCode('.tagName)==="INPUT"||'), @@ -114,7 +115,7 @@ export default definePlugin({ // Fix silly Discord calling the non web support copy { match: /\i\.\i\.copy/, - replace: "Vencord.Webpack.Common.Clipboard.copy" + replace: "Vencord.Util.copyToClipboard" } ] }, @@ -223,7 +224,7 @@ export default definePlugin({ }, { match: /\i\.\i\.copy(?=\(\i)/, - replace: "Vencord.Webpack.Common.Clipboard.copy" + replace: "Vencord.Util.copyToClipboard" } ], all: true, @@ -288,7 +289,7 @@ export default definePlugin({ const selection = document.getSelection(); if (!selection) return; - Clipboard.copy(selection.toString()); + copyToClipboard(selection.toString()); }, cut() { diff --git a/src/utils/clipboard.ts b/src/utils/clipboard.ts new file mode 100644 index 00000000..c098a549 --- /dev/null +++ b/src/utils/clipboard.ts @@ -0,0 +1,9 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export function copyToClipboard(text: string): Promise { + return IS_DISCORD_DESKTOP ? DiscordNative.clipboard.copy(text) : navigator.clipboard.writeText(text); +} diff --git a/src/utils/index.ts b/src/utils/index.ts index ed347fdc..70ac9d42 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -19,6 +19,7 @@ export * from "../shared/debounce"; export * from "../shared/onceDefined"; export * from "./ChangeList"; +export * from "./clipboard"; export * from "./constants"; export * from "./discord"; export * from "./guards"; diff --git a/src/utils/misc.ts b/src/utils/misc.ts index adca15d3..7f9f6e59 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -16,8 +16,9 @@ * along with this program. If not, see . */ -import { Clipboard, Toasts } from "@webpack/common"; +import { Toasts } from "@webpack/common"; +import { copyToClipboard } from "./clipboard"; import { DevsById } from "./constants"; /** @@ -35,12 +36,8 @@ export function sleep(ms: number): Promise { return new Promise(r => setTimeout(r, ms)); } -export function copyWithToast(text: string, toastMessage = "Copied to clipboard!") { - if (Clipboard.SUPPORTS_COPY) { - Clipboard.copy(text); - } else { - toastMessage = "Your browser does not support copying to clipboard"; - } +export async function copyWithToast(text: string, toastMessage = "Copied to clipboard!") { + await copyToClipboard(text); Toasts.show({ message: toastMessage, id: Toasts.genId(), diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index f5535b19..0396f0f3 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -149,11 +149,6 @@ export const ApplicationAssetUtils = mapMangledModuleLazy("getAssetImage: size m getAssets: filters.byCode(".assets") }); -export const Clipboard: t.Clipboard = mapMangledModuleLazy('queryCommandEnabled("copy")', { - copy: filters.byCode(".copy("), - SUPPORTS_COPY: e => typeof e === "boolean" -}); - export const NavigationRouter: t.NavigationRouter = mapMangledModuleLazy("Transitioning to ", { transitionTo: filters.byCode("transitionTo -"), transitionToGuild: filters.byCode("transitionToGuild -"), From e99e89e964da257adefc795e03a7bce0ca794654 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Wed, 16 Apr 2025 11:57:49 -0700 Subject: [PATCH 037/198] MessageLogger: correctly ignore venbot in all vc support channels (#3384) Co-authored-by: Vending Machine --- src/plugins/messageLogger/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 4bce7f5e..ffe5286e 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -23,7 +23,7 @@ import { updateMessage } from "@api/MessageUpdater"; import { Settings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; -import { Devs } from "@utils/constants"; +import { Devs, SUPPORT_CATEGORY_ID, VENBOT_USER_ID } from "@utils/constants"; import { getIntlMessage } from "@utils/discord"; import { Logger } from "@utils/Logger"; import { classes } from "@utils/misc"; @@ -295,8 +295,8 @@ export default definePlugin({ ignoreChannels.includes(ChannelStore.getChannel(message.channel_id)?.parent_id) || (isEdit ? !logEdits : !logDeletes) || ignoreGuilds.includes(ChannelStore.getChannel(message.channel_id)?.guild_id) || - // Ignore Venbot in the support channel - (message.channel_id === "1026515880080842772" && message.author?.id === "1017176847865352332"); + // Ignore Venbot in the support channels + (message.author?.id === VENBOT_USER_ID && ChannelStore.getChannel(message.channel_id)?.parent_id === SUPPORT_CATEGORY_ID); }, EditMarker({ message, className, children, ...props }: any) { From 506e3dcfd65f4fa742a546342da2f22f017ed25b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 16 Apr 2025 22:52:50 -0400 Subject: [PATCH 038/198] Clipboard Fixes --- src/equicordplugins/commandPalette/commands.tsx | 7 ++++--- src/equicordplugins/copyProfileColors/index.tsx | 5 +++-- src/equicordplugins/copyUserMention/index.tsx | 5 +++-- src/equicordplugins/exportContacts/index.tsx | 5 +++-- src/equicordplugins/friendCodes/FriendCodesPanel.tsx | 5 +++-- src/equicordplugins/gifCollections/index.tsx | 5 +++-- src/equicordplugins/glide/index.tsx | 12 ++++-------- .../holyNotes/components/modals/RenderMessage.tsx | 9 +++++---- src/equicordplugins/iconViewer/rawModal.tsx | 5 ++--- src/equicordplugins/iconViewer/subComponents.tsx | 4 ++-- .../themeLibrary/components/ThemeInfoModal.tsx | 5 +++-- src/equicordplugins/themeLibrary/utils/settings.tsx | 5 +++-- 12 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/equicordplugins/commandPalette/commands.tsx b/src/equicordplugins/commandPalette/commands.tsx index 3b78ec12..c324c90b 100644 --- a/src/equicordplugins/commandPalette/commands.tsx +++ b/src/equicordplugins/commandPalette/commands.tsx @@ -6,9 +6,10 @@ import { showNotification } from "@api/Notifications"; import { Settings } from "@api/Settings"; +import { copyToClipboard } from "@utils/clipboard"; import { relaunch, showItemInFolder } from "@utils/native"; import { checkForUpdates, getRepo } from "@utils/updater"; -import { Clipboard, GuildStore, NavigationRouter, SettingsRouter, Toasts } from "@webpack/common"; +import { GuildStore, NavigationRouter, SettingsRouter, Toasts } from "@webpack/common"; import gitHash from "~git-hash"; import gitRemote from "~git-remote"; @@ -89,7 +90,7 @@ export const actions: ButtonAction[] = [ const newUrl = url.replace(/(https?:\/\/)?([a-zA-Z0-9-]+)\.([a-zA-Z0-9-]+)/, "https://$2.$3"); const res = (await fetch(newUrl)); const text = await res.text(); - Clipboard.copy(text); + copyToClipboard(text); Toasts.show({ message: "Copied response to clipboard!", @@ -115,7 +116,7 @@ export const actions: ButtonAction[] = [ { id: "copyGitInfo", label: "Copy Git Info", callback: async () => { - Clipboard.copy(`gitHash: ${gitHash}\ngitRemote: ${gitRemote}`); + copyToClipboard(`gitHash: ${gitHash}\ngitRemote: ${gitRemote}`); Toasts.show({ message: "Copied git info to clipboard!", diff --git a/src/equicordplugins/copyProfileColors/index.tsx b/src/equicordplugins/copyProfileColors/index.tsx index 216adacf..2a3ab604 100644 --- a/src/equicordplugins/copyProfileColors/index.tsx +++ b/src/equicordplugins/copyProfileColors/index.tsx @@ -5,9 +5,10 @@ */ import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { copyToClipboard } from "@utils/clipboard"; import { EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { Clipboard, Menu, Toasts, UserProfileStore } from "@webpack/common"; +import { Menu, Toasts, UserProfileStore } from "@webpack/common"; function getProfileColors(userId) { try { @@ -46,7 +47,7 @@ function copyProfileColors(userId) { const formattedColors = `Primary-color #${primaryColor}, Secondary-Color #${secondaryColor}`; try { - Clipboard.copy(formattedColors); + copyToClipboard(formattedColors); Toasts.show({ type: Toasts.Type.SUCCESS, message: "Profile colors copied to clipboard!", diff --git a/src/equicordplugins/copyUserMention/index.tsx b/src/equicordplugins/copyUserMention/index.tsx index 81423265..c8e03d8e 100644 --- a/src/equicordplugins/copyUserMention/index.tsx +++ b/src/equicordplugins/copyUserMention/index.tsx @@ -5,9 +5,10 @@ */ import { NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { copyToClipboard } from "@utils/clipboard"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { Clipboard, Menu } from "@webpack/common"; +import { Menu } from "@webpack/common"; import type { Channel, User } from "discord-types/general"; const MentionIcon = () => ( @@ -37,7 +38,7 @@ const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: U Clipboard.copy(`<@${user.id}>`)} + action={() => copyToClipboard(`<@${user.id}>`)} icon={MentionIcon} /> ); diff --git a/src/equicordplugins/exportContacts/index.tsx b/src/equicordplugins/exportContacts/index.tsx index a454e432..6a5df39f 100644 --- a/src/equicordplugins/exportContacts/index.tsx +++ b/src/equicordplugins/exportContacts/index.tsx @@ -19,9 +19,10 @@ import "./styles.css"; import ErrorBoundary from "@components/ErrorBoundary"; +import { copyToClipboard } from "@utils/clipboard"; import { EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { Clipboard, Toasts } from "@webpack/common"; +import { Toasts } from "@webpack/common"; interface User { id: string; @@ -97,7 +98,7 @@ export default definePlugin({ copyContactToClipboard() { if (this.contactList) { - Clipboard.copy(JSON.stringify(this.contactList)); + copyToClipboard(JSON.stringify(this.contactList)); Toasts.show({ message: "Contacts copied to clipboard successfully.", type: Toasts.Type.SUCCESS, diff --git a/src/equicordplugins/friendCodes/FriendCodesPanel.tsx b/src/equicordplugins/friendCodes/FriendCodesPanel.tsx index b1832579..10e071ab 100644 --- a/src/equicordplugins/friendCodes/FriendCodesPanel.tsx +++ b/src/equicordplugins/friendCodes/FriendCodesPanel.tsx @@ -6,8 +6,9 @@ import "./styles.css"; +import { copyToClipboard } from "@utils/clipboard"; import { findByPropsLazy } from "@webpack"; -import { Button, Clipboard, Flex, Forms, Parser, Text, useEffect, useState } from "@webpack/common"; +import { Button, Flex, Forms, Parser, Text, useEffect, useState } from "@webpack/common"; import { FriendInvite } from "./types"; @@ -51,7 +52,7 @@ function FriendInviteCard({ invite }: { invite: FriendInvite; }) { Clipboard.copy(`https://discord.gg/${invite.code}`)} + onClick={() => copyToClipboard(`https://discord.gg/${invite.code}`)} /> diff --git a/src/equicordplugins/gifCollections/index.tsx b/src/equicordplugins/gifCollections/index.tsx index 33fff6ac..c6a09822 100644 --- a/src/equicordplugins/gifCollections/index.tsx +++ b/src/equicordplugins/gifCollections/index.tsx @@ -10,10 +10,11 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; import { Flex } from "@components/Flex"; +import { copyToClipboard } from "@utils/clipboard"; import { Devs, EquicordDevs } from "@utils/constants"; import { ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; -import { Alerts, Button, Clipboard, ContextMenuApi, FluxDispatcher, Forms, Menu, React, showToast, TextInput, Toasts, useCallback, useState } from "@webpack/common"; +import { Alerts, Button, ContextMenuApi, FluxDispatcher, Forms, Menu, React, showToast, TextInput, Toasts, useCallback, useState } from "@webpack/common"; import { addToCollection, cache_collections, createCollection, DATA_COLLECTION_NAME, deleteCollection, fixPrefix, getCollections, getGifById, getItemCollectionNameFromId, moveGifToCollection, refreshCacheCollection, removeFromCollection, renameCollection } from "./utils/collectionManager"; import { getFormat } from "./utils/getFormat"; @@ -526,7 +527,7 @@ const RemoveItemContextMenu = ({ type, nameOrId, instance }) => ( action={() => { const gifInfo = getGifById(nameOrId); if (!gifInfo) return; - Clipboard.copy(gifInfo.url); + copyToClipboard(gifInfo.url); showToast("URL copied to clipboard", Toasts.Type.SUCCESS); }} /> diff --git a/src/equicordplugins/glide/index.tsx b/src/equicordplugins/glide/index.tsx index 5547133a..70ca5d4c 100644 --- a/src/equicordplugins/glide/index.tsx +++ b/src/equicordplugins/glide/index.tsx @@ -5,10 +5,11 @@ */ import { definePluginSettings, Settings } from "@api/Settings"; +import { copyToClipboard } from "@utils/clipboard"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType, StartAt } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; -import { Button, Clipboard, Forms, TextInput, Toasts, useState } from "@webpack/common"; +import { Button, Forms, TextInput, Toasts, useState } from "@webpack/common"; import { darkenColorHex, generateRandomColorHex, saturateColorHex } from "./generateTheme"; import { themes } from "./themeDefinitions"; @@ -58,10 +59,7 @@ function copyPreset(name: string) { name: "${name}" } `; - if (Clipboard.SUPPORTS_COPY) { - Clipboard.copy(template); - } - + copyToClipboard(template); } function CopyPresetComponent() { @@ -229,9 +227,7 @@ export function ColorPick({ propertyname }: { propertyname: string; }) { function copyCSS() { - if (Clipboard.SUPPORTS_COPY) { - Clipboard.copy(getCSS(parseFontContent())); - } + copyToClipboard(getCSS(parseFontContent())); } function parseFontContent() { diff --git a/src/equicordplugins/holyNotes/components/modals/RenderMessage.tsx b/src/equicordplugins/holyNotes/components/modals/RenderMessage.tsx index 2308ede3..45edbf7c 100644 --- a/src/equicordplugins/holyNotes/components/modals/RenderMessage.tsx +++ b/src/equicordplugins/holyNotes/components/modals/RenderMessage.tsx @@ -4,10 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { copyToClipboard } from "@utils/clipboard"; import { classes } from "@utils/misc"; import { ModalProps } from "@utils/modal"; import { findByCode, findByCodeLazy, findByProps, findComponentByCodeLazy } from "@webpack"; -import { Clipboard, ContextMenuApi, FluxDispatcher, Menu, NavigationRouter, React } from "@webpack/common"; +import { ContextMenuApi, FluxDispatcher, Menu, NavigationRouter, React } from "@webpack/common"; import noteHandler from "../../NoteHandler"; import { HolyNotes } from "../../types"; @@ -139,13 +140,13 @@ const NoteContextMenu = ( Clipboard.copy(note.content)} + action={() => copyToClipboard(note.content)} /> {note?.attachments.length ? ( Clipboard.copy(note.attachments[0].url)} + action={() => copyToClipboard(note.attachments[0].url)} />) : null} Clipboard.copy(note.id)} + action={() => copyToClipboard(note.id)} /> ); diff --git a/src/equicordplugins/iconViewer/rawModal.tsx b/src/equicordplugins/iconViewer/rawModal.tsx index 7bcfa3af..f6092a19 100644 --- a/src/equicordplugins/iconViewer/rawModal.tsx +++ b/src/equicordplugins/iconViewer/rawModal.tsx @@ -5,6 +5,7 @@ */ import { CodeBlock } from "@components/CodeBlock"; +import { copyToClipboard } from "@utils/clipboard"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { @@ -41,9 +42,7 @@ function ModalComponent(props: { func: Function; iconName: string; color: number color={Button.Colors.PRIMARY} className={"vc-iv-raw-modal-copy-button"} onClick={() => { - // silly typescript - // @ts-ignore - Clipboard.copy(String(func)); + copyToClipboard(String(func)); Toasts.show({ id: Toasts.genId(), message: `Copied raw \`${iconName}\` to clipboard`, diff --git a/src/equicordplugins/iconViewer/subComponents.tsx b/src/equicordplugins/iconViewer/subComponents.tsx index abb655c9..4132df17 100644 --- a/src/equicordplugins/iconViewer/subComponents.tsx +++ b/src/equicordplugins/iconViewer/subComponents.tsx @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { copyToClipboard } from "@utils/clipboard"; import { getIntlMessage } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; @@ -27,8 +28,7 @@ export type ClickableProps = Prop export function IconTooltip({ children, copy, className, ...props }: ClickableProps & { children: string; copy: string; }) { return { - // @ts-ignore - Clipboard.copy(copy); + copyToClipboard(copy); }} {...props}>{children} ; } diff --git a/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx b/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx index 1cba1e18..5954c06e 100644 --- a/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx +++ b/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx @@ -6,13 +6,14 @@ import { CodeBlock } from "@components/CodeBlock"; import { Heart } from "@components/Heart"; +import { copyToClipboard } from "@utils/clipboard"; import { openInviteModal } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; import type { PluginNative } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; -import { Button, Clipboard, Forms, Parser, React, showToast, Toasts } from "@webpack/common"; +import { Button, Forms, Parser, React, showToast, Toasts } from "@webpack/common"; import { Theme, ThemeInfoModalProps } from "../types"; import { ClockIcon, DownloadIcon, WarningIcon } from "../utils/Icons"; @@ -145,7 +146,7 @@ export const ThemeInfoModal: React.FC = ({ author, theme, . diff --git a/src/equicordplugins/themeLibrary/utils/settings.tsx b/src/equicordplugins/themeLibrary/utils/settings.tsx index a535ca5a..939d51dd 100644 --- a/src/equicordplugins/themeLibrary/utils/settings.tsx +++ b/src/equicordplugins/themeLibrary/utils/settings.tsx @@ -8,8 +8,9 @@ import * as DataStore from "@api/DataStore"; import { definePluginSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; import { OpenExternalIcon } from "@components/Icons"; +import { copyToClipboard } from "@utils/clipboard"; import { OptionType } from "@utils/types"; -import { Button, Clipboard, Forms, Toasts } from "@webpack/common"; +import { Button, Forms, Toasts } from "@webpack/common"; import { authorizeUser, deauthorizeUser } from "./auth"; @@ -39,7 +40,7 @@ export const settings = definePluginSettings({ } }); - Clipboard.copy(token); + copyToClipboard(token); Toasts.show({ message: "Copied to Clipboard!", From 59ebdcb8606c391ac1328f86d084656d5cb0133c Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 16 Apr 2025 22:55:09 -0400 Subject: [PATCH 039/198] Fix Formatting --- src/plugins/messageLogger/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index dfd0788f..d58bf636 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -14,7 +14,7 @@ import { updateMessage } from "@api/MessageUpdater"; import { Settings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; -import { Devs, SUPPORT_CATEGORY_ID, VC_SUPPORT_CATEGORY_ID, VENBOT_USER_ID } from "@utils/constants"; +import { Devs, VC_SUPPORT_CATEGORY_ID, VENBOT_USER_ID } from "@utils/constants"; import { getIntlMessage } from "@utils/discord"; import { Logger } from "@utils/Logger"; import { classes } from "@utils/misc"; From 1a8ea79d485a7a9b2d25e8bfc65ce6400345ed33 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 17 Apr 2025 07:46:59 -0400 Subject: [PATCH 040/198] BannersEverywhere Fix --- src/equicordplugins/bannersEverywhere/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/bannersEverywhere/index.tsx b/src/equicordplugins/bannersEverywhere/index.tsx index ab3e8da0..f402dd3f 100644 --- a/src/equicordplugins/bannersEverywhere/index.tsx +++ b/src/equicordplugins/bannersEverywhere/index.tsx @@ -58,7 +58,7 @@ export default definePlugin({ replacement: [ { // We add the banner as a property while we can still access the user id - match: /verified:(\i).isVerifiedBot.*?nameplate:(\i).*?name:null.*?(?=avatar:)/, + match: /user:(\i).{0,150}nameplate:(\i).*?name:null.*?(?=avatar:)/, replace: "$&banner:$self.memberListBannerHook($1, $2),", }, { From 9476e259fcf86dbe285ec9a4fa6cf99675000dc1 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 17 Apr 2025 16:38:41 -0400 Subject: [PATCH 041/198] Fix & Restrict QuestCompleter --- .../index.tsx | 26 ++----------------- .../style.css | 0 2 files changed, 2 insertions(+), 24 deletions(-) rename src/equicordplugins/{questCompleter => questCompleter.discordDesktop}/index.tsx (92%) rename src/equicordplugins/{questCompleter => questCompleter.discordDesktop}/style.css (100%) diff --git a/src/equicordplugins/questCompleter/index.tsx b/src/equicordplugins/questCompleter.discordDesktop/index.tsx similarity index 92% rename from src/equicordplugins/questCompleter/index.tsx rename to src/equicordplugins/questCompleter.discordDesktop/index.tsx index 83b62788..bd85c6d0 100644 --- a/src/equicordplugins/questCompleter/index.tsx +++ b/src/equicordplugins/questCompleter.discordDesktop/index.tsx @@ -17,17 +17,14 @@ */ import "@equicordplugins/_misc/styles.css"; +import "./style.css"; import { showNotification } from "@api/Notifications"; import { Devs } from "@utils/constants"; import { getTheme, Theme } from "@utils/discord"; import definePlugin from "@utils/types"; import { findByProps, findComponentByCodeLazy } from "@webpack"; -import { Button, ChannelStore, FluxDispatcher, Forms, GuildChannelStore, NavigationRouter, RestAPI, Tooltip, UserStore } from "@webpack/common"; - -const isApp = typeof DiscordNative !== "undefined"; - -import "./style.css"; +import { Button, ChannelStore, FluxDispatcher, GuildChannelStore, NavigationRouter, RestAPI, Tooltip, UserStore } from "@webpack/common"; const QuestIcon = findComponentByCodeLazy("10.47a.76.76"); @@ -89,13 +86,6 @@ async function openCompleteQuestUI() { }); console.log(`Spoofing video for ${applicationName}.`); } else if (taskName === "PLAY_ON_DESKTOP") { - if (!isApp) { - showNotification({ - title: `${applicationName} - Quest Completer`, - body: `${applicationName}'s quest requires the desktop app.`, - icon: icon, - }); - } RestAPI.get({ url: `/applications/public?application_ids=${applicationId}` }).then(res => { const appData = res.body[0]; const exeName = appData.executables.find(x => x.os === "win32").name.replace(">", ""); @@ -147,13 +137,6 @@ async function openCompleteQuestUI() { console.log(`Spoofed your game to ${applicationName}.`); }); } else if (taskName === "STREAM_ON_DESKTOP") { - if (!isApp) { - showNotification({ - title: `${applicationName} - Quest Completer`, - body: `${applicationName}'s quest requires the desktop app.`, - icon: icon, - }); - } const stream = ApplicationStreamingStore.getAnyStreamForUser(UserStore.getCurrentUser()?.id); if (!stream) { showNotification({ @@ -234,11 +217,6 @@ export default definePlugin({ name: "QuestCompleter", description: "A plugin to complete quests without having the game installed.", authors: [Devs.amia], - settingsAboutComponent: () => <> - - Game Quests do not work on Equibop/Web Platforms. Only Video Quests do. - - , patches: [ { find: "AppTitleBar", diff --git a/src/equicordplugins/questCompleter/style.css b/src/equicordplugins/questCompleter.discordDesktop/style.css similarity index 100% rename from src/equicordplugins/questCompleter/style.css rename to src/equicordplugins/questCompleter.discordDesktop/style.css From aefd056c98ebd4c715896ddd946a13aee9733d2e Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:14:20 -0400 Subject: [PATCH 042/198] Revert Some Reporter Stuff --- scripts/generateReport.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index a709f7bc..da9bd53d 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -315,11 +315,9 @@ page.on("console", async e => { report.badWebpackFinds.push(otherMessage); break; case "Finished test": + await browser.close(); await printReport(); - setTimeout(async () => { - await browser.close(); - process.exit(); - }, 10000); + process.exit(); } } } From fd901950938e523d75f4b3d7ccb6f22b74ddf490 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:16:09 -0400 Subject: [PATCH 043/198] Increase Timeout --- scripts/generateReport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index da9bd53d..77acfd13 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -356,4 +356,4 @@ await page.evaluateOnNewDocument(` } `); -await page.goto(CANARY ? "https://canary.discord.com/login" : "https://discord.com/login"); +await page.goto(CANARY ? "https://canary.discord.com/login" : "https://discord.com/login", { timeout: 120000 }); From e620e46ebee2f306fe518053fb6565ac953d9d41 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:50:16 -0400 Subject: [PATCH 044/198] Remove Some Functionality From BetterBlockUsers Temp --- .../betterBlockedUsers/index.tsx | 51 +++++++------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/src/equicordplugins/betterBlockedUsers/index.tsx b/src/equicordplugins/betterBlockedUsers/index.tsx index b3d64864..f1a5efb5 100644 --- a/src/equicordplugins/betterBlockedUsers/index.tsx +++ b/src/equicordplugins/betterBlockedUsers/index.tsx @@ -10,7 +10,7 @@ import { openUserProfile } from "@utils/discord"; import { openModal } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import { Button, FluxDispatcher, React, RelationshipStore, Text, TextInput, UserStore } from "@webpack/common"; +import { Button, FluxDispatcher, React, RelationshipStore, showToast, Text, TextInput, UserStore } from "@webpack/common"; import { ButtonProps } from "@webpack/types"; import { User } from "discord-types/general"; @@ -27,12 +27,6 @@ const settings = definePluginSettings({ type: OptionType.BOOLEAN, description: "Adds a 'View DMs' button to the users in the blocked/ignored list.", }, - hideBlockedWarning: { - default: false, - type: OptionType.BOOLEAN, - description: "Skip the warning about blocked/ignored users when opening any profile anywhere on discord outside of the blocklist.", - restartNeeded: true, - }, showUnblockConfirmation: { default: true, type: OptionType.BOOLEAN, @@ -49,6 +43,11 @@ const settings = definePluginSettings({ type: OptionType.BOOLEAN, description: "Color the unblock button in the blocklist red instead of gray.", }, + allowShiftUnblock: { + default: true, + type: OptionType.BOOLEAN, + description: "Unblock a user without confirmation prompting when holding shift.", + } }); export default definePlugin({ @@ -84,6 +83,7 @@ export default definePlugin({ }, { find: "#{intl::OUTGOING_FRIEND_REQUEST}", + group: true, replacement: [ { match: /(?<=return \i)\|\|(\i)===.*?.FRIEND/, @@ -95,26 +95,6 @@ export default definePlugin({ } ], }, - { - find: ',["user"])', - replacement: { - match: /(?<=isIgnored:.*?,\[\i,\i]=\i.useState\()\i\|\|\i\|\|\i.*?]\);/, - replace: "false);" - }, - }, - - // If the users wishes to, they can disable the warning in all other places as well. - ...[ - "UserProfilePanelWrapper: currentUser cannot be undefined", - "UserProfilePopoutWrapper: currentUser cannot be undefined", - ].map(x => ({ - find: x, - replacement: { - match: /(?<=isIgnored:.*?,\[\i,\i]=\i.useState\()\i\|\|\i\|\|\i\)(?:;\i.useEffect.*?]\))?/, - replace: "false)", - }, - predicate: () => settings.store.hideBlockedWarning, - })), { find: ".BLOCKED:return", @@ -185,8 +165,6 @@ export default definePlugin({ if (settings.store.unblockButtonDanger) originalProps.color = Button.Colors.RED; - // TODO add extra unblock confirmation after the click + setting. - if (settings.store.showUnblockConfirmation || settings.store.showUnblockConfirmationEverywhere) { const originalOnClick = originalProps.onClick!; originalProps.onClick = e => { @@ -208,12 +186,19 @@ export default definePlugin({ }, openDMChannel(user: User) { - ChannelActions.openPrivateChannel(user.id); + try { + ChannelActions.openPrivateChannel(user.id); + } + catch (e) { + showToast("Failed to open DMs for user '" + user.username + "'! Check the console for more info"); + return console.error(e); + } + // only close the settings window if we actually opened a DM channel behind it. this.closeSettingsWindow(); - return null; }, + openConfirmationModal(event: MouseEvent, callback: () => any, user: User | string, isSettingsOrigin: boolean = false) { - if (event.shiftKey) return callback(); + if (event.shiftKey && settings.store.allowShiftUnblock) return callback(); if (typeof user === "string") { user = UserStore.getUser(user); @@ -222,7 +207,7 @@ export default definePlugin({ return openModal(m => { From 179b56e57f6e69b41099c49777e0ae409f3c98ce Mon Sep 17 00:00:00 2001 From: Crxaw <48805031+sitescript@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:25:34 +0100 Subject: [PATCH 045/198] Added toggles for Sticker links / emoji links (#234) * Added toggles for Sticker links / emoji links * Redo Setting Names --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- src/plugins/fakeNitro/index.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 46f15f85..6adcb032 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -167,8 +167,13 @@ const settings = definePluginSettings({ default: true, restartNeeded: true }, - useHyperLinks: { - description: "Whether to use hyperlinks when sending fake emojis and stickers", + useStickerHyperLinks: { + description: "Whether to use hyperlinks when sending fake stickers", + type: OptionType.BOOLEAN, + default: true + }, + useEmojiHyperLinks: { + description: "Whether to use hyperlinks when sending fake emojis", type: OptionType.BOOLEAN, default: true }, @@ -910,7 +915,7 @@ export default definePlugin({ const linkText = s.hyperLinkText.replaceAll("{{NAME}}", sticker.name); - messageObj.content += `${getWordBoundary(messageObj.content, messageObj.content.length - 1)}${s.useHyperLinks ? `[${linkText}](${url})` : url}`; + messageObj.content += `${getWordBoundary(messageObj.content, messageObj.content.length - 1)}${s.useStickerHyperLinks ? `[${linkText}](${url})` : url}`; extra.stickers!.length = 0; } } @@ -933,7 +938,7 @@ export default definePlugin({ const linkText = s.hyperLinkText.replaceAll("{{NAME}}", emoji.name); messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => { - return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${linkText}](${url})` : url}${getWordBoundary(origStr, offset + match.length)}`; + return `${getWordBoundary(origStr, offset - 1)}${s.useEmojiHyperLinks ? `[${linkText}](${url})` : url}${getWordBoundary(origStr, offset + match.length)}`; }); } } @@ -965,7 +970,7 @@ export default definePlugin({ const linkText = s.hyperLinkText.replaceAll("{{NAME}}", emoji.name); - return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${linkText}](${url})` : url}${getWordBoundary(origStr, offset + emojiStr.length)}`; + return `${getWordBoundary(origStr, offset - 1)}${s.useEmojiHyperLinks ? `[${linkText}](${url})` : url}${getWordBoundary(origStr, offset + emojiStr.length)}`; }); if (hasBypass && !s.disableEmbedPermissionCheck && !hasEmbedPerms(channelId)) { From 851fc84e66371a5481610384281b78481ebf32a9 Mon Sep 17 00:00:00 2001 From: ItsAlex <87634014+ItsAlexIK@users.noreply.github.com> Date: Sat, 19 Apr 2025 02:37:05 +0200 Subject: [PATCH 046/198] Encryptcord Fix (#235) * Encryptcord Fix * FixName * Update index.tsx * Update constants.ts * Update constants.ts * Update index.tsx * Final fix --- src/equicordplugins/encryptcord/index.tsx | 27 ++++++++++++++++------- src/utils/constants.ts | 4 ++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/equicordplugins/encryptcord/index.tsx b/src/equicordplugins/encryptcord/index.tsx index a125e777..1f260ca4 100644 --- a/src/equicordplugins/encryptcord/index.tsx +++ b/src/equicordplugins/encryptcord/index.tsx @@ -12,7 +12,7 @@ import { } from "@api/Commands"; import * as DataStore from "@api/DataStore"; import { addMessagePreSendListener, MessageSendListener, removeMessagePreSendListener } from "@api/MessageEvents"; -import { Devs } from "@utils/constants"; +import { Devs, EquicordDevs } from "@utils/constants"; import { sleep } from "@utils/misc"; import definePlugin from "@utils/types"; import { @@ -140,7 +140,7 @@ const ChatBarIcon: ChatBarButtonFactory = ({ isMainChat }) => { export default definePlugin({ name: "Encryptcord", description: "End-to-end encryption in Discord!", - authors: [Devs.Inbestigator], + authors: [Devs.Inbestigator, EquicordDevs.ItsAlex], patches: [ { find: "INTERACTION_APPLICATION_COMMAND_INVALID_VERSION", @@ -360,18 +360,28 @@ async function handleGroupData(groupData) { // Handle joining group async function handleJoin(senderId: string, senderKey: string, encryptcordGroupMembers: object) { - encryptcordGroupMembers[senderId] = { key: senderKey, parent: UserStore.getCurrentUser().id, child: null }; - encryptcordGroupMembers[UserStore.getCurrentUser().id].child = senderId; + const currentUserId = UserStore.getCurrentUser().id; + + if (!encryptcordGroupMembers[senderId]) { + encryptcordGroupMembers[senderId] = { key: senderKey, parent: currentUserId, child: null }; + } + + if (!encryptcordGroupMembers[currentUserId]) { + encryptcordGroupMembers[currentUserId] = { key: "", parent: null, child: null }; + } + encryptcordGroupMembers[currentUserId].child = senderId; + await DataStore.set("encryptcordGroupMembers", encryptcordGroupMembers); + const groupChannel = await DataStore.get("encryptcordChannelId"); + const newMember = await UserUtils.getUser(senderId).catch(() => null); if (!newMember) return; const membersData = {}; - Object.entries(encryptcordGroupMembers) - .forEach(([memberId, value]) => { - membersData[memberId] = value; - }); + Object.entries(encryptcordGroupMembers).forEach(([memberId, value]) => { + membersData[memberId] = value; + }); const membersDataString = JSON.stringify({ members: membersData, channel: groupChannel }); @@ -382,6 +392,7 @@ async function handleJoin(senderId: string, senderKey: string, encryptcordGroupM }); await Promise.all(dmPromises); + await MessageActions.receiveMessage(groupChannel, { ...await createMessage("", senderId, groupChannel, 7), components: [{ type: 1, diff --git a/src/utils/constants.ts b/src/utils/constants.ts index a8cf16d7..e8686a82 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1042,6 +1042,10 @@ export const EquicordDevs = Object.freeze({ name: "smuki", id: 691517398523576331n }, + ItsAlex: { + name: "ItsAlex", + id: 551023598203043840n + }, } satisfies Record); // iife so #__PURE__ works correctly From bb8a68b45e31f3e847fc7b8dee87c02e9d95f912 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 18 Apr 2025 23:49:51 -0400 Subject: [PATCH 047/198] Drop HideServers --- README.md | 1 - .../hideServers/HiddenServersStore.ts | 58 ------- .../components/HiddenServersButton.tsx | 37 ----- .../components/HiddenServersMenu.tsx | 105 ------------- .../hideServers/components/style.css | 47 ------ src/equicordplugins/hideServers/index.tsx | 142 ------------------ src/equicordplugins/hideServers/settings.tsx | 51 ------- src/plugins/_api/serverList.ts | 4 - 8 files changed, 445 deletions(-) delete mode 100644 src/equicordplugins/hideServers/HiddenServersStore.ts delete mode 100644 src/equicordplugins/hideServers/components/HiddenServersButton.tsx delete mode 100644 src/equicordplugins/hideServers/components/HiddenServersMenu.tsx delete mode 100644 src/equicordplugins/hideServers/components/style.css delete mode 100644 src/equicordplugins/hideServers/index.tsx delete mode 100644 src/equicordplugins/hideServers/settings.tsx diff --git a/README.md b/README.md index f0b87abb..0f92fb32 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - GlobalBadges by HypedDomi & Hosted by Wolfie - GoogleThat by Samwich - HideChatButtons by iamme -- HideServers by bepvte - HolyNotes by Wolfie - HomeTyping by Samwich - HopOn by ImLvna diff --git a/src/equicordplugins/hideServers/HiddenServersStore.ts b/src/equicordplugins/hideServers/HiddenServersStore.ts deleted file mode 100644 index bdad64fd..00000000 --- a/src/equicordplugins/hideServers/HiddenServersStore.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import * as DataStore from "@api/DataStore"; -import { findStoreLazy, proxyLazyWebpack } from "@webpack"; -import { Flux, FluxDispatcher, GuildStore } from "@webpack/common"; -import { Guild } from "discord-types/general"; - - -export const HiddenServersStore = proxyLazyWebpack(() => { - const { Store } = Flux; - - const SortedGuildStore = findStoreLazy("SortedGuildStore"); - const DB_KEY = "HideServers_servers"; - - class HiddenServersStore extends Store { - private _hiddenGuilds: Set = new Set(); - public get hiddenGuilds() { - return this._hiddenGuilds; - } - // id try to use .initialize() but i dont know how it works - public async load() { - const data = await DataStore.get(DB_KEY); - if (data) { - this._hiddenGuilds = data; - } - } - public unload() { - this._hiddenGuilds.clear(); - } - - public addHidden(guild: Guild) { - this._hiddenGuilds.add(guild.id); - DataStore.set(DB_KEY, this._hiddenGuilds); - this.emitChange(); - } - public removeHidden(id: string) { - this._hiddenGuilds.delete(id); - DataStore.set(DB_KEY, this._hiddenGuilds); - this.emitChange(); - } - public clearHidden() { - this._hiddenGuilds.clear(); - DataStore.del(DB_KEY); - this.emitChange(); - } - public hiddenGuildsDetail(): Guild[] { - const sortedGuildIds = SortedGuildStore.getFlattenedGuildIds() as string[]; - // otherwise the list is in order of increasing id number which is confusing - return sortedGuildIds.filter(id => this._hiddenGuilds.has(id)).map(id => GuildStore.getGuild(id)); - } - } - - return new HiddenServersStore(FluxDispatcher); -}); diff --git a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx deleted file mode 100644 index d9c25408..00000000 --- a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import "./style.css"; - -import { classNameFactory } from "@api/Styles"; -import { Button, GuildStore, useStateFromStores } from "@webpack/common"; - -import { HiddenServersStore } from "../HiddenServersStore"; -import { openHiddenServersModal } from "./HiddenServersMenu"; - -const cl = classNameFactory("vc-hideservers-"); - -function HiddenServersButton() { - const hiddenGuilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); - // if youve left a server dont show it in the count - const actuallyHidden = Array.from(hiddenGuilds).filter(x => GuildStore.getGuild(x)).length; - return ( -
- {actuallyHidden > 0 ? ( - - ) : null} -
- ); -} - -export default () => { return ; }; diff --git a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx deleted file mode 100644 index 2673e1d4..00000000 --- a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { classNameFactory } from "@api/Styles"; -import { classes } from "@utils/misc"; -import { - closeModal, - ModalCloseButton, - ModalContent, - ModalHeader, - ModalProps, - ModalRoot, - ModalSize, - openModal, -} from "@utils/modal"; -import { findByPropsLazy } from "@webpack"; -import { Button, Forms, IconUtils, Text, useStateFromStores } from "@webpack/common"; -import { Guild } from "discord-types/general"; - -import { HiddenServersStore } from "../HiddenServersStore"; - -const cl = classNameFactory("vc-hideservers-"); -const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); - -function HiddenServersModal({ - modalProps, - close, -}: { - modalProps: ModalProps; - close(): void; -}) { - const servers = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail()); - return ( - - - - Hidden Servers - - - - - - - - - ); -} - -export function HiddenServersMenu({ servers }: { servers: Guild[]; }) { - return
- {servers.length > 0 ? ( - servers.map(server => ( -
-
- {server.icon - ? - :
- {server.acronym} -
- } -
- - {server.name} - - -
- )) - ) : ( - - No hidden servers - - )} -
; -} - -export function openHiddenServersModal() { - const key = openModal(modalProps => { - return ( - closeModal(key)} - /> - ); - }); -} diff --git a/src/equicordplugins/hideServers/components/style.css b/src/equicordplugins/hideServers/components/style.css deleted file mode 100644 index ee553841..00000000 --- a/src/equicordplugins/hideServers/components/style.css +++ /dev/null @@ -1,47 +0,0 @@ -.vc-hideservers-button-wrapper { - display: flex; - justify-content: center; - margin: 0 0 8px; -} - -.vc-hideservers-button { - max-width: 48px; - font-size: 60%; - background-color: var(--background-primary); - color: var(--header-secondary); -} - -.vc-hideservers-list { - flex: 1 1 auto; -} - -/* copied from blocked row */ -.vc-hideservers-row { - height: 62px; - display: flex; - align-items: center; - flex-direction: row; - margin-left: 20px; - margin-right: 20px; - border-top: 1px solid var(--background-modifier-accent); - border-bottom: 1px solid transparent; - justify-content: space-between; -} - -.vc-hideservers-guildicon { - display: flex; - align-items: center; - margin-right: 1em; -} - -.vc-hideservers-guildicon img { - border-radius: 50%; -} - -.vc-hideservers-name { - flex-grow: 1; -} - -.vc-hideservers-row-button { - margin-left: auto; -} diff --git a/src/equicordplugins/hideServers/index.tsx b/src/equicordplugins/hideServers/index.tsx deleted file mode 100644 index 0d8aded7..00000000 --- a/src/equicordplugins/hideServers/index.tsx +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -// additional thanks to mwittrien/DevilBro and nexpid for their server hiding plugins, which served as inspiration - -import { - findGroupChildrenByChildId, - NavContextMenuPatchCallback, -} from "@api/ContextMenu"; -import { - addServerListElement, - removeServerListElement, - ServerListRenderPosition, -} from "@api/ServerList"; -import { EquicordDevs } from "@utils/constants"; -import definePlugin from "@utils/types"; -import { Menu, React, useStateFromStores } from "@webpack/common"; -import { Guild } from "discord-types/general"; - -import hiddenServersButton from "./components/HiddenServersButton"; -import { HiddenServersStore } from "./HiddenServersStore"; -import settings from "./settings"; - -type guildsNode = { - type: "guild" | "folder"; - id: number | string; - children: guildsNode[]; -}; - -type qsResult = { - type: "GUILD" | string; - record?: { - id?: string; - guild_id?: string; - }; -}; - -const Patch: NavContextMenuPatchCallback = ( - children, - { guild }: { guild: Guild; } -) => { - const group = findGroupChildrenByChildId("privacy", children); - - group?.push( - HiddenServersStore.addHidden(guild)} - /> - ); -}; - -export function addIndicator() { - addServerListElement(ServerListRenderPosition.Below, hiddenServersButton); -} - -export function removeIndicator() { - removeServerListElement(ServerListRenderPosition.Below, hiddenServersButton); -} - -export default definePlugin({ - name: "HideServers", - description: "Allows you to hide servers from the guild list and quick switcher by right clicking them", - authors: [EquicordDevs.bep], - tags: ["guild", "server", "hide"], - - dependencies: ["ServerListAPI"], - contextMenus: { - "guild-context": Patch, - "guild-header-popout": Patch, - }, - patches: [ - { - find: '("guildsnav")', - replacement: [ - { - match: /(?<=#{intl::SERVERS}\),children:)(\i)(\)?\.map\(\i\))/g, - replace: "$self.useFilteredGuilds($1)$2", - }, - // despite my best efforts, the above doesnt trigger a rerender - { - match: /let{disableAppDownload.{0,10}isPlatformEmbedded/, - replace: "$self.useStore();$&", - } - ] - }, - { - find: "#{intl::QUICKSWITCHER_PROTIP}", - replacement: { - match: /(?<=renderResults\(\){)let{query/, - replace: "this.props.results = $self.filteredGuildResults(this.props.results);$&", - }, - }, - ], - settings, - useStore: () => { useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); }, - - async start() { - if (settings.store.showIndicator) { - addIndicator(); - } - await HiddenServersStore.load(); - }, - - async stop() { - removeIndicator(); - HiddenServersStore.unload(); - }, - - useFilteredGuilds(guilds: guildsNode[]): guildsNode[] { - const hiddenGuilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); - return guilds.flatMap(guild => { - if (!(hiddenGuilds instanceof Set)) return [guild]; - if (hiddenGuilds.has(guild.id.toString())) { - return []; - } - const newGuild = Object.assign({}, guild); - newGuild.children = guild.children.filter( - child => !hiddenGuilds.has(child.id.toString()) - ); - - return [newGuild]; - }); - }, - - filteredGuildResults(results: qsResult[]): qsResult[] { - // not used in a component so no useStateFromStore - const { hiddenGuilds } = HiddenServersStore; - return results.filter(result => { - if (result?.record?.guild_id && hiddenGuilds.has(result.record.guild_id)) { - return false; - } - if (result.type === "GUILD" && hiddenGuilds.has(result.record!.id!)) { - return false; - } - return true; - }); - }, -}); diff --git a/src/equicordplugins/hideServers/settings.tsx b/src/equicordplugins/hideServers/settings.tsx deleted file mode 100644 index 1f684824..00000000 --- a/src/equicordplugins/hideServers/settings.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { definePluginSettings } from "@api/Settings"; -import { OptionType } from "@utils/types"; -import { Button, useStateFromStores } from "@webpack/common"; - -import { addIndicator, removeIndicator } from "."; -import { HiddenServersMenu } from "./components/HiddenServersMenu"; -import { HiddenServersStore } from "./HiddenServersStore"; - -export default definePluginSettings({ - showIndicator: { - type: OptionType.BOOLEAN, - description: "Show menu to unhide servers at the bottom of the list", - default: true, - onChange: val => { - if (val) { - addIndicator(); - } else { - removeIndicator(); - } - } - }, - guildsList: { - type: OptionType.COMPONENT, - description: "Remove hidden servers", - component: () => { - const detail = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail()); - return ; - } - }, - resetHidden: { - type: OptionType.COMPONENT, - description: "Remove all hidden guilds from the list", - component: () => ( -
- -
- ), - }, -}); diff --git a/src/plugins/_api/serverList.ts b/src/plugins/_api/serverList.ts index 37ed626a..8dd4bd39 100644 --- a/src/plugins/_api/serverList.ts +++ b/src/plugins/_api/serverList.ts @@ -37,10 +37,6 @@ export default definePlugin({ { match: /(?<=#{intl::SERVERS}\),children:)\i\.map\(\i\)/, replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" - }, - { - match: /children:\i\}\)\]/, - replace: "$&.concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Below))" } ] } From 4a6e009a008834cacae912689c579845e0261dd6 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:45:01 -0400 Subject: [PATCH 048/198] Fix Dono Button --- src/components/DonateButton.tsx | 19 +++++++++++++++++++ src/plugins/_api/badges/index.tsx | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/components/DonateButton.tsx b/src/components/DonateButton.tsx index 4139dd31..9ab439ee 100644 --- a/src/components/DonateButton.tsx +++ b/src/components/DonateButton.tsx @@ -22,6 +22,25 @@ import { ButtonProps } from "@webpack/types"; import { Heart } from "./Heart"; +export default function VCDonateButton({ + look = Button.Looks.LINK, + color = Button.Colors.TRANSPARENT, + ...props +}: Partial) { + return ( + + ); +} + export default function DonateButton({ look = Button.Looks.LINK, color = Button.Colors.TRANSPARENT, diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index 8b5ff999..b4f26206 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -19,6 +19,7 @@ import "./fixDiscordBadgePadding.css"; import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; +import VCDonateButton from "@components/DonateButton"; import DonateButton from "@components/DonateButton"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; @@ -213,7 +214,7 @@ export default definePlugin({ - + From 8c4af321bf71b7428d3533f32acd2dcc790c9436 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 19 Apr 2025 14:19:23 -0400 Subject: [PATCH 049/198] Fix Lint --- src/components/DonateButton.tsx | 2 +- src/plugins/_api/badges/index.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/DonateButton.tsx b/src/components/DonateButton.tsx index 9ab439ee..0d96276c 100644 --- a/src/components/DonateButton.tsx +++ b/src/components/DonateButton.tsx @@ -22,7 +22,7 @@ import { ButtonProps } from "@webpack/types"; import { Heart } from "./Heart"; -export default function VCDonateButton({ +export function VCDonateButton({ look = Button.Looks.LINK, color = Button.Colors.TRANSPARENT, ...props diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index b4f26206..e8ecb43b 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -19,8 +19,7 @@ import "./fixDiscordBadgePadding.css"; import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; -import VCDonateButton from "@components/DonateButton"; -import DonateButton from "@components/DonateButton"; +import DonateButton, { VCDonateButton } from "@components/DonateButton"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Heart } from "@components/Heart"; From d01ea8aaff39f2da97f178bf0def2f06b094e7c8 Mon Sep 17 00:00:00 2001 From: Riley Date: Mon, 21 Apr 2025 11:23:05 -0500 Subject: [PATCH 050/198] Add choice prompt for if user wants to update installer or not. (#239) This commit adds a choice prompt for if a user wishes to update their Equicord installer or not. This also serves as a bandaid fix for people on metered connections always updating the Equicord installer when they do not mean to. Co-authored-by: Riley --- misc/install.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) mode change 100644 => 100755 misc/install.sh diff --git a/misc/install.sh b/misc/install.sh old mode 100644 new mode 100755 index 08fe8bdd..d245733f --- a/misc/install.sh +++ b/misc/install.sh @@ -62,8 +62,16 @@ check_for_updates() { local_modified=$(stat -c "%y" "$INSTALLER_PATH" | cut -d' ' -f1-2) || error "Failed to get local modified date" if [ "$local_modified" != "$latest_modified" ]; then - echo -e "${YELLOW}Installer is outdated. Updating...${NC}" - download_installer + echo -e "${YELLOW}Installer is outdated. Do you wish to update? [y/n]${NC}" + read -p "" -n 1 -r retval + + # Create a new line before printing our next notice, otherwise it will be printed on the same line + # that the prompt was created on! + echo "" + case "$retval" in + y|Y ) download_installer;; + n|N ) echo -e "${YELLOW}Update cancelled. Running installer...${NC}" && return;; + esac else echo -e "${GREEN}Installer is up-to-date.${NC}" fi From ccbe5c16f1500e37cc9d50c4e6835d3ee46b3cb1 Mon Sep 17 00:00:00 2001 From: byeoon <47872200+byeoon@users.noreply.github.com> Date: Mon, 21 Apr 2025 17:03:16 +0000 Subject: [PATCH 051/198] feat(plugin): CopyStickerLinks (#240) * add awesome swag plugin * Update README.md * Update pnpm-lock.yaml --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- README.md | 3 +- src/plugins/copyStickerLinks/index.tsx | 147 +++++++++++++++++++++++++ src/utils/constants.ts | 6 +- 3 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/plugins/copyStickerLinks/index.tsx diff --git a/README.md b/README.md index 0f92fb32..c1fe6b51 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-166 additional plugins +167 additional plugins ### All Platforms @@ -38,6 +38,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - CleanChannelName by AutumnVN - ClientSideBlock by Samwich - CommandPalette by Ethan +- CopyStickerLinks by Byeoon - CopyUserMention by Cortex & castdrian - CustomSounds by TheKodeToad & SpikeHD - CustomTimestamps by Rini & nvhrr diff --git a/src/plugins/copyStickerLinks/index.tsx b/src/plugins/copyStickerLinks/index.tsx new file mode 100644 index 00000000..f98a3c96 --- /dev/null +++ b/src/plugins/copyStickerLinks/index.tsx @@ -0,0 +1,147 @@ +/* +* Vencord, a modification for Discord's desktop app +* Copyright (c) 2025 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 . +*/ + +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { Devs } from "@utils/constants"; +import { copyWithToast } from "@utils/misc"; +import definePlugin from "@utils/types"; +import { findStoreLazy } from "@webpack"; +import { Menu, React } from "@webpack/common"; +import { Promisable } from "type-fest"; + +const StickersStore = findStoreLazy("StickersStore"); + +interface Sticker { + t: "Sticker"; + format_type: number; + id: string; + type: number; +} + +const StickerExt = [, "png", "png", "json", "gif"] as const; + +function getUrl(data: Sticker) { + if (data.format_type === 4) + return `https:${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.gif?size=4096&lossless=true`; + + return `https://${window.GLOBAL_ENV.CDN_HOST}/stickers/${data.id}.${StickerExt[data.format_type]}?size=4096&lossless=true`; +} + +function buildMenuItem(Sticker, fetchData: () => Promisable>) { + return ( + <> + + + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data[0]); + copyWithToast(url, "Link copied!"); + } + } + /> + + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data[0]); + VencordNative.native.openExternal(url); + } + } + /> + + ); +} + +function buildMenuExpression(Sticker, fetchData: () => Promisable>) { + return ( + <> + + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data); + copyWithToast(url, "Link copied!"); + } + } + /> + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data); + VencordNative.native.openExternal(url); + } + } + /> + + ); +} + +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { + const { favoriteableId, favoriteableType } = props ?? {}; + if (!favoriteableId) return; + const menuItem = (() => { + const sticker = props.message.stickerItems.find(s => s.id === favoriteableId); + if (sticker?.format_type === 3) return; + switch (favoriteableType) { + case "sticker": + return buildMenuItem("Sticker", () => props.message.stickerItems); + } + })(); + + if (menuItem) + findGroupChildrenByChildId("devmode-copy-id", children, true)?.push(menuItem); +}; + +const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { target: HTMLElement; }) => { + const { id } = props?.target?.dataset ?? {}; + if (!id) return; + + if (!props.target.className?.includes("lottieCanvas")) { + const stickerCache = StickersStore.getStickerById(id); + if (stickerCache) { + children.push(buildMenuExpression("Sticker", () => stickerCache)); + } + } +}; + +export default definePlugin({ + name: "CopyStickerLinks", + description: "Adds the ability to copy and open sticker links to your browser", + authors: [Devs.Byeoon], + contextMenus: { + "message": messageContextMenuPatch, + "expression-picker": expressionPickerPatch + } +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index e8686a82..f8aabc88 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -494,7 +494,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Sqaaakoi", id: 259558259491340288n }, - Byron: { + Byeoon: { name: "byeoon", id: 1167275288036655133n }, @@ -1046,6 +1046,10 @@ export const EquicordDevs = Object.freeze({ name: "ItsAlex", id: 551023598203043840n }, + Byeoon: { + name: "byeoon", + id: 1167275288036655133n + }, } satisfies Record); // iife so #__PURE__ works correctly From a068fb8e8a1ad13f3290f4cc03bd1b8d2a72737b Mon Sep 17 00:00:00 2001 From: bep <8226605+bepvte@users.noreply.github.com> Date: Mon, 21 Apr 2025 16:33:02 -0700 Subject: [PATCH 052/198] fix hideservers (#241) * Revert "Drop HideServers" This reverts commit bb8a68b45e31f3e847fc7b8dee87c02e9d95f912. * Fix HideServers --- README.md | 1 + .../hideServers/HiddenServersStore.ts | 58 +++++++ .../components/HiddenServersButton.tsx | 37 +++++ .../components/HiddenServersMenu.tsx | 105 +++++++++++++ .../hideServers/components/style.css | 47 ++++++ src/equicordplugins/hideServers/index.tsx | 142 ++++++++++++++++++ src/equicordplugins/hideServers/settings.tsx | 51 +++++++ src/plugins/_api/serverList.ts | 10 +- 8 files changed, 448 insertions(+), 3 deletions(-) create mode 100644 src/equicordplugins/hideServers/HiddenServersStore.ts create mode 100644 src/equicordplugins/hideServers/components/HiddenServersButton.tsx create mode 100644 src/equicordplugins/hideServers/components/HiddenServersMenu.tsx create mode 100644 src/equicordplugins/hideServers/components/style.css create mode 100644 src/equicordplugins/hideServers/index.tsx create mode 100644 src/equicordplugins/hideServers/settings.tsx diff --git a/README.md b/README.md index c1fe6b51..0562fef0 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - GlobalBadges by HypedDomi & Hosted by Wolfie - GoogleThat by Samwich - HideChatButtons by iamme +- HideServers by bepvte - HolyNotes by Wolfie - HomeTyping by Samwich - HopOn by ImLvna diff --git a/src/equicordplugins/hideServers/HiddenServersStore.ts b/src/equicordplugins/hideServers/HiddenServersStore.ts new file mode 100644 index 00000000..2f7a4f93 --- /dev/null +++ b/src/equicordplugins/hideServers/HiddenServersStore.ts @@ -0,0 +1,58 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import * as DataStore from "@api/DataStore"; +import { findStoreLazy, proxyLazyWebpack } from "@webpack"; +import { Flux, FluxDispatcher, GuildStore } from "@webpack/common"; +import { Guild } from "discord-types/general"; + + +export const HiddenServersStore = proxyLazyWebpack(() => { + const { Store } = Flux; + + const SortedGuildStore = findStoreLazy("SortedGuildStore"); + const DB_KEY = "HideServers_servers"; + + class HiddenServersStore extends Store { + private _hiddenGuilds: Set = new Set(); + public get hiddenGuilds() { + return this._hiddenGuilds; + } + // id try to use .initialize() but i dont know how it works + public async load() { + const data = await DataStore.get(DB_KEY); + if (data && data instanceof Set) { + this._hiddenGuilds = data; + } + } + public unload() { + this._hiddenGuilds.clear(); + } + + public addHidden(guild: Guild) { + this._hiddenGuilds.add(guild.id); + DataStore.set(DB_KEY, this._hiddenGuilds); + this.emitChange(); + } + public removeHidden(id: string) { + this._hiddenGuilds.delete(id); + DataStore.set(DB_KEY, this._hiddenGuilds); + this.emitChange(); + } + public clearHidden() { + this._hiddenGuilds.clear(); + DataStore.del(DB_KEY); + this.emitChange(); + } + public hiddenGuildsDetail(): Guild[] { + const sortedGuildIds = SortedGuildStore.getFlattenedGuildIds() as string[]; + // otherwise the list is in order of increasing id number which is confusing + return sortedGuildIds.filter(id => this._hiddenGuilds.has(id)).map(id => GuildStore.getGuild(id)); + } + } + + return new HiddenServersStore(FluxDispatcher); +}); diff --git a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx new file mode 100644 index 00000000..d9c25408 --- /dev/null +++ b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx @@ -0,0 +1,37 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./style.css"; + +import { classNameFactory } from "@api/Styles"; +import { Button, GuildStore, useStateFromStores } from "@webpack/common"; + +import { HiddenServersStore } from "../HiddenServersStore"; +import { openHiddenServersModal } from "./HiddenServersMenu"; + +const cl = classNameFactory("vc-hideservers-"); + +function HiddenServersButton() { + const hiddenGuilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); + // if youve left a server dont show it in the count + const actuallyHidden = Array.from(hiddenGuilds).filter(x => GuildStore.getGuild(x)).length; + return ( +
+ {actuallyHidden > 0 ? ( + + ) : null} +
+ ); +} + +export default () => { return ; }; diff --git a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx new file mode 100644 index 00000000..2673e1d4 --- /dev/null +++ b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx @@ -0,0 +1,105 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { classNameFactory } from "@api/Styles"; +import { classes } from "@utils/misc"; +import { + closeModal, + ModalCloseButton, + ModalContent, + ModalHeader, + ModalProps, + ModalRoot, + ModalSize, + openModal, +} from "@utils/modal"; +import { findByPropsLazy } from "@webpack"; +import { Button, Forms, IconUtils, Text, useStateFromStores } from "@webpack/common"; +import { Guild } from "discord-types/general"; + +import { HiddenServersStore } from "../HiddenServersStore"; + +const cl = classNameFactory("vc-hideservers-"); +const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); + +function HiddenServersModal({ + modalProps, + close, +}: { + modalProps: ModalProps; + close(): void; +}) { + const servers = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail()); + return ( + + + + Hidden Servers + + + + + + + + + ); +} + +export function HiddenServersMenu({ servers }: { servers: Guild[]; }) { + return
+ {servers.length > 0 ? ( + servers.map(server => ( +
+
+ {server.icon + ? + :
+ {server.acronym} +
+ } +
+ + {server.name} + + +
+ )) + ) : ( + + No hidden servers + + )} +
; +} + +export function openHiddenServersModal() { + const key = openModal(modalProps => { + return ( + closeModal(key)} + /> + ); + }); +} diff --git a/src/equicordplugins/hideServers/components/style.css b/src/equicordplugins/hideServers/components/style.css new file mode 100644 index 00000000..ee553841 --- /dev/null +++ b/src/equicordplugins/hideServers/components/style.css @@ -0,0 +1,47 @@ +.vc-hideservers-button-wrapper { + display: flex; + justify-content: center; + margin: 0 0 8px; +} + +.vc-hideservers-button { + max-width: 48px; + font-size: 60%; + background-color: var(--background-primary); + color: var(--header-secondary); +} + +.vc-hideservers-list { + flex: 1 1 auto; +} + +/* copied from blocked row */ +.vc-hideservers-row { + height: 62px; + display: flex; + align-items: center; + flex-direction: row; + margin-left: 20px; + margin-right: 20px; + border-top: 1px solid var(--background-modifier-accent); + border-bottom: 1px solid transparent; + justify-content: space-between; +} + +.vc-hideservers-guildicon { + display: flex; + align-items: center; + margin-right: 1em; +} + +.vc-hideservers-guildicon img { + border-radius: 50%; +} + +.vc-hideservers-name { + flex-grow: 1; +} + +.vc-hideservers-row-button { + margin-left: auto; +} diff --git a/src/equicordplugins/hideServers/index.tsx b/src/equicordplugins/hideServers/index.tsx new file mode 100644 index 00000000..8c44ee7e --- /dev/null +++ b/src/equicordplugins/hideServers/index.tsx @@ -0,0 +1,142 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +// additional thanks to mwittrien/DevilBro and nexpid for their server hiding plugins, which served as inspiration + +import { + findGroupChildrenByChildId, + NavContextMenuPatchCallback, +} from "@api/ContextMenu"; +import { + addServerListElement, + removeServerListElement, + ServerListRenderPosition, +} from "@api/ServerList"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { Menu, React, useStateFromStores } from "@webpack/common"; +import { Guild } from "discord-types/general"; + +import hiddenServersButton from "./components/HiddenServersButton"; +import { HiddenServersStore } from "./HiddenServersStore"; +import settings from "./settings"; + +type guildsNode = { + type: "guild" | "folder"; + id: number | string; + children: guildsNode[]; +}; + +type qsResult = { + type: "GUILD" | string; + record?: { + id?: string; + guild_id?: string; + }; +}; + +const Patch: NavContextMenuPatchCallback = ( + children, + { guild }: { guild: Guild; } +) => { + const group = findGroupChildrenByChildId("privacy", children); + + group?.push( + HiddenServersStore.addHidden(guild)} + /> + ); +}; + +export function addIndicator() { + addServerListElement(ServerListRenderPosition.Below, hiddenServersButton); +} + +export function removeIndicator() { + removeServerListElement(ServerListRenderPosition.Below, hiddenServersButton); +} + +export default definePlugin({ + name: "HideServers", + description: "Allows you to hide servers from the guild list and quick switcher by right clicking them", + authors: [EquicordDevs.bep], + tags: ["guild", "server", "hide"], + + dependencies: ["ServerListAPI"], + contextMenus: { + "guild-context": Patch, + "guild-header-popout": Patch, + }, + patches: [ + { + find: '("guildsnav")', + replacement: [ + { + match: /(?<=#{intl::SERVERS}\),gap:"xs",children:.{0,100}?)(\i)(\.map\(.{5,30}\}\))/, + replace: "$self.useFilteredGuilds($1)$2" + }, + // despite my best efforts, the above doesnt trigger a rerender + { + match: /let{disableAppDownload.{0,10}isPlatformEmbedded/, + replace: "$self.useStore();$&", + } + ] + }, + { + find: "#{intl::QUICKSWITCHER_PROTIP}", + replacement: { + match: /(?<=renderResults\(\){)let{query/, + replace: "this.props.results = $self.filteredGuildResults(this.props.results);$&", + }, + }, + ], + settings, + useStore: () => { useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); }, + + async start() { + if (settings.store.showIndicator) { + addIndicator(); + } + await HiddenServersStore.load(); + }, + + async stop() { + removeIndicator(); + HiddenServersStore.unload(); + }, + + useFilteredGuilds(guilds: guildsNode[]): guildsNode[] { + const hiddenGuilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); + return guilds.flatMap(guild => { + if (!(hiddenGuilds instanceof Set)) return [guild]; + if (hiddenGuilds.has(guild.id.toString())) { + return []; + } + const newGuild = Object.assign({}, guild); + newGuild.children = guild.children.filter( + child => !hiddenGuilds.has(child.id.toString()) + ); + + return [newGuild]; + }); + }, + + filteredGuildResults(results: qsResult[]): qsResult[] { + // not used in a component so no useStateFromStore + const { hiddenGuilds } = HiddenServersStore; + return results.filter(result => { + if (result?.record?.guild_id && hiddenGuilds.has(result.record.guild_id)) { + return false; + } + if (result.type === "GUILD" && hiddenGuilds.has(result.record!.id!)) { + return false; + } + return true; + }); + }, +}); diff --git a/src/equicordplugins/hideServers/settings.tsx b/src/equicordplugins/hideServers/settings.tsx new file mode 100644 index 00000000..1f684824 --- /dev/null +++ b/src/equicordplugins/hideServers/settings.tsx @@ -0,0 +1,51 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { OptionType } from "@utils/types"; +import { Button, useStateFromStores } from "@webpack/common"; + +import { addIndicator, removeIndicator } from "."; +import { HiddenServersMenu } from "./components/HiddenServersMenu"; +import { HiddenServersStore } from "./HiddenServersStore"; + +export default definePluginSettings({ + showIndicator: { + type: OptionType.BOOLEAN, + description: "Show menu to unhide servers at the bottom of the list", + default: true, + onChange: val => { + if (val) { + addIndicator(); + } else { + removeIndicator(); + } + } + }, + guildsList: { + type: OptionType.COMPONENT, + description: "Remove hidden servers", + component: () => { + const detail = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail()); + return ; + } + }, + resetHidden: { + type: OptionType.COMPONENT, + description: "Remove all hidden guilds from the list", + component: () => ( +
+ +
+ ), + }, +}); diff --git a/src/plugins/_api/serverList.ts b/src/plugins/_api/serverList.ts index 8dd4bd39..edc25b21 100644 --- a/src/plugins/_api/serverList.ts +++ b/src/plugins/_api/serverList.ts @@ -32,11 +32,15 @@ export default definePlugin({ } }, { - find: "#{intl::SERVERS}),children", + find: "#{intl::SERVERS}),gap:\"xs\",children:", replacement: [ { - match: /(?<=#{intl::SERVERS}\),children:)\i\.map\(\i\)/, - replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" + match: /(?<=#{intl::SERVERS}\),gap:"xs",children:)(\i\.map\(.{10,50}?)(}\))/, + replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($1)$2" + }, + { + match: /children:.{0,2000}?\{\}\)\]/, + replace: "$&.concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Below))" } ] } From 186e7b5595c5a0fd0aaf06a9be54e5f5f98d8f0c Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Mon, 21 Apr 2025 23:30:40 -0400 Subject: [PATCH 053/198] Patches --- src/equicordplugins/channelTabs/index.tsx | 2 +- .../_api/memberListDecorators/index.tsx | 6 +++- .../accountPanelServerProfile/index.tsx | 19 ++++++----- src/plugins/betterFolders/index.tsx | 33 ++++++++++++------- src/plugins/forceOwnerCrown/index.ts | 3 +- src/plugins/noUnblockToJump/README.md | 5 --- src/plugins/noUnblockToJump/index.ts | 2 +- src/plugins/reviewDB/index.tsx | 3 +- src/plugins/showHiddenChannels/index.tsx | 14 ++++---- 9 files changed, 51 insertions(+), 36 deletions(-) delete mode 100644 src/plugins/noUnblockToJump/README.md diff --git a/src/equicordplugins/channelTabs/index.tsx b/src/equicordplugins/channelTabs/index.tsx index f6895140..6c5aaf10 100644 --- a/src/equicordplugins/channelTabs/index.tsx +++ b/src/equicordplugins/channelTabs/index.tsx @@ -48,7 +48,7 @@ export default definePlugin({ { find: ".COLLECTIBLES_SHOP_FULLSCREEN))", replacement: { - match: /(\?void 0:(\i)\.channelId.{0,300}return)((.{0,15})"div",{.*?\])(\}\)\}\})/, + match: /(\?void 0:(\i)\.channelId.{0,500}return)((.{0,15})"div",{.*?\])(\}\)\}\})/, replace: "$1$4$self.render,{currentChannel:$2,children:$3})$5" } }, diff --git a/src/plugins/_api/memberListDecorators/index.tsx b/src/plugins/_api/memberListDecorators/index.tsx index 365c21f3..3f76a42f 100644 --- a/src/plugins/_api/memberListDecorators/index.tsx +++ b/src/plugins/_api/memberListDecorators/index.tsx @@ -36,9 +36,13 @@ export default definePlugin({ match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/, replace: "$&vencordProps=$1," }, + { + match: /decorators:.{0,100}?(?=user:)/, + replace: "$&vencordProps:typeof vencordProps!=='undefined'?vencordProps:void 0," + }, { match: /children:\[(?=.{0,300},lostPermissionTooltipText:)/, - replace: "children:[(typeof vencordProps!=='undefined'&&Vencord.Api.MemberListDecorators.__getDecorators(vencordProps))," + replace: "children:[(arguments[0]?.vencordProps&&Vencord.Api.MemberListDecorators.__getDecorators(arguments[0].vencordProps))," } ] }, diff --git a/src/plugins/accountPanelServerProfile/index.tsx b/src/plugins/accountPanelServerProfile/index.tsx index 8b561581..536cf6e4 100644 --- a/src/plugins/accountPanelServerProfile/index.tsx +++ b/src/plugins/accountPanelServerProfile/index.tsx @@ -9,7 +9,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { getCurrentChannel } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findComponentByCodeLazy } from "@webpack"; import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common"; import { User } from "discord-types/general"; @@ -19,8 +19,7 @@ interface UserProfileProps { originalRenderPopout: () => React.ReactNode; } -const UserProfile = findComponentByCodeLazy("UserProfilePopoutWrapper: user cannot be undefined"); -const styles = findByPropsLazy("accountProfilePopoutWrapper"); +const UserProfile = findComponentByCodeLazy(".BITE_SIZE,user:"); let openAlternatePopout = false; let accountPanelRef: React.RefObject | null> = { current: null }; @@ -77,7 +76,7 @@ export default definePlugin({ replace: "$self.useAccountPanelRef();$&" }, { - match: /(\.AVATAR,children:.+?renderPopout:(\(\i,\i\))=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/, + match: /(\.AVATAR,children:.+?renderPopout:\((\i),\i\)=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/, replace: (_, rest, popoutProps, originalPopout, currentUser) => `${rest}$self.UserProfile({popoutProps:${popoutProps},currentUser:${currentUser},originalRenderPopout:()=>{${originalPopout}}})` }, { @@ -121,14 +120,18 @@ export default definePlugin({ } const currentChannel = getCurrentChannel(); - if (currentChannel?.getGuildId() == null) { + if (currentChannel?.getGuildId() == null || !UserProfile.$$vencordGetWrappedComponent()) { return originalRenderPopout(); } return ( -
- -
+ ); }, { noop: true }) }); diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 2d8bf360..4143d293 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -124,27 +124,42 @@ export default definePlugin({ replacement: [ // Create the isBetterFolders variable in the GuildsBar component { - match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i,)/, + match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i,)/g, replace: "$&,isBetterFolders" }, // If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders { - match: /\[(\i)\]=(\(0,\i\.\i\).{0,40}getGuildsTree\(\).+?}\))(?=,)/, + match: /\[(\i)\]=(\(0,\i\.\i\).{0,40}getGuildsTree\(\).+?}\))(?=,)/g, replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0]?.isBetterFolders,betterFoldersOriginalTree,arguments[0]?.betterFoldersExpandedIds)` }, // If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children { - match: /lastTargetNode:\i\[\i\.length-1\].+?}\)(?::null)?\](?=}\))/, + match: /lastTargetNode:\i\[\i\.length-1\].+?}\)(?::null)?\](?=}\))/g, replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0]?.isBetterFolders))" }, // If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children + // As of now, this is just the unread indicator at the bottom + // Discord has two different sidebars controlled by an experiment + // only the second one needs this filter { - match: /unreadMentionsIndicatorBottom,.+?}\)\]/, + match: /topSection.+?unreadMentionsIndicatorBottom,.+?}\)\]/, replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0]?.isBetterFolders))" }, + // With one of the sidebar versions, there is a sticky top bar. Don't render it if we are rendering the Better Folders sidebar + { + // [^0] to not match any other JSX call + match: /(?=\(0,\i\.jsxs?\)[^0]+\.topSection)/, + replace: "!!arguments[0]?.isBetterFolders?null:" + }, + // Don't render the tiny separator line at the top of the Better Folders sidebar + // Only needed with the sidebar variant with the sticky top bar + { + match: /(?=\(0,\i\.jsxs?\)[^0]+fullWidth:)/, + replace: "!!arguments[0]?.isBetterFolders?null:" + }, // Export the isBetterFolders variable to the folders component { - match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/, + match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/g, replace: '$&isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,' } ] @@ -327,13 +342,7 @@ export default definePlugin({ return child => { if (!isBetterFolders) return true; - if (child?.props?.className?.includes("itemsContainer") && child.props.children != null) { - // Filter out everything but the scroller for the guild list - child.props.children = child.props.children.filter(child => child?.props?.onScroll != null); - return true; - } - - return false; + return child?.props?.className?.includes("itemsContainer") && child.props.children != null; }; }, diff --git a/src/plugins/forceOwnerCrown/index.ts b/src/plugins/forceOwnerCrown/index.ts index 907d9dc0..df0243d8 100644 --- a/src/plugins/forceOwnerCrown/index.ts +++ b/src/plugins/forceOwnerCrown/index.ts @@ -29,7 +29,8 @@ export default definePlugin({ { find: "#{intl::GUILD_OWNER}),children:", replacement: { - match: /,isOwner:(\i),/, + // The isOwner prop is used in more tha one component in this module. Make sure we patch the right one + match: /,isOwner:(\i),(?=[^}]+guildId)/, replace: ",_isOwner:$1=$self.isGuildOwner(e)," } } diff --git a/src/plugins/noUnblockToJump/README.md b/src/plugins/noUnblockToJump/README.md deleted file mode 100644 index 326bca3b..00000000 --- a/src/plugins/noUnblockToJump/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# No Unblock To Jump - -Removes the popup preventing you to jump to a message from a blocked/ignored user (eg: in search results) - -![A modal popup telling you to unblock a user to jump their message](https://github.com/user-attachments/assets/0e4b859d-f3b3-4101-9a83-829afb473d1e) diff --git a/src/plugins/noUnblockToJump/index.ts b/src/plugins/noUnblockToJump/index.ts index 04ddf2ed..4dbbe462 100644 --- a/src/plugins/noUnblockToJump/index.ts +++ b/src/plugins/noUnblockToJump/index.ts @@ -30,7 +30,7 @@ export default definePlugin({ find: '.id,"Search Results"', replacement: [ { - match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/, + match: /if\(.{1,40}\)(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/, replace: "if(false)$1" }, { diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index 822ebde6..858b82f3 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -91,7 +91,8 @@ export default definePlugin({ } }, { - find: 'location:"UserProfilePanel"', + // location: "UserProfileSiebar" + find: ".PANEL,children:[", replacement: { match: /{profileType:\i\.\i\.PANEL,children:\[/, replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 08f7fbf1..21598633 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -357,7 +357,7 @@ export default definePlugin({ ] }, { - find: '})},"overflow"))', + find: "overflowCountVariant", replacement: [ { // Create a variable for the channel prop @@ -366,17 +366,19 @@ export default definePlugin({ }, { // Make Discord always render the plus button if the component is used inside the HiddenChannelLockScreen - match: /\i>0(?=&&.{0,60}renderPopout)/, + match: /\i>0(?=&&.{0,60}Math.min)/, replace: m => `($self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)?true:${m})` }, { - // Prevent Discord from overwriting the last children with the plus button if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen - match: /(?<=\.value\(\),(\i)=.+?length-)1(?=\]=.{0,60}renderPopout)/, + // Prevent Discord from overwriting the last children with the plus button + // if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen + match: /(?<=\i\.length-)1(?=\]=.{0,60}renderPopout)(?<=(\i)=\i\.length-\i.*)/, replace: (_, amount) => `($self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)&&${amount}<=0?0:1)` }, { - // Show only the plus text without overflowed children amount if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen - match: /(?<="\+",)(\i)\+1/, + // Show only the plus text without overflowed children amount + // if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen + match: /(?<="\+"\.concat\()(\i)/, replace: (m, amount) => `$self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)&&${amount}<=0?"":${m}` } ] From cf78ddcfe27e48d3c49e6a12a4bf1834ae6ea29c Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Wed, 23 Apr 2025 14:36:43 -0400 Subject: [PATCH 054/198] fix plugins for latest discord update (#3387) Co-authored-by: Vendicated --- src/api/MemberListDecorators.tsx | 31 ++++++------------- src/plugins/_api/badges/index.tsx | 2 +- .../_api/memberListDecorators/index.tsx | 10 ++---- src/plugins/_api/serverList.ts | 4 +-- .../accountPanelServerProfile/index.tsx | 19 +++++++----- src/plugins/forceOwnerCrown/index.ts | 4 +-- src/plugins/friendsSince/index.tsx | 2 +- src/plugins/ircColors/index.ts | 4 +-- src/plugins/mutualGroupDMs/index.tsx | 8 +++++ src/plugins/mutualGroupDMs/style.css | 4 +++ src/plugins/noUnblockToJump/index.ts | 2 +- 11 files changed, 44 insertions(+), 46 deletions(-) create mode 100644 src/plugins/mutualGroupDMs/style.css diff --git a/src/api/MemberListDecorators.tsx b/src/api/MemberListDecorators.tsx index ab5a618b..ada60776 100644 --- a/src/api/MemberListDecorators.tsx +++ b/src/api/MemberListDecorators.tsx @@ -21,25 +21,14 @@ import { Channel, User } from "discord-types/general/index.js"; import { JSX } from "react"; 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; + type: "guild" | "dm"; user: User; - [key: string]: any; + /** only present when this is a DM list item */ + channel: Channel; + /** only present when this is a guild list item */ + isOwner: boolean; } + export type MemberListDecoratorFactory = (props: DecoratorProps) => JSX.Element | null; type OnlyIn = "guilds" | "dms"; @@ -53,18 +42,16 @@ export function removeMemberListDecorator(identifier: string) { decoratorsFactories.delete(identifier); } -export function __getDecorators(props: DecoratorProps): JSX.Element { - const isInGuild = !!(props.guildId); - +export function __getDecorators(props: DecoratorProps, type: "guild" | "dm"): JSX.Element { const decorators = Array.from( decoratorsFactories.entries(), ([key, { render: Decorator, onlyIn }]) => { - if ((onlyIn === "guilds" && !isInGuild) || (onlyIn === "dms" && isInGuild)) + if ((onlyIn === "guilds" && type !== "guild") || (onlyIn === "dms" && type !== "dm")) return null; return ( - + ); } diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index e63073ce..52bede44 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -63,7 +63,7 @@ export default definePlugin({ required: true, patches: [ { - find: ".FULL_SIZE]:26", + find: ".MODAL]:26", replacement: { match: /(?=;return 0===(\i)\.length\?)(?<=(\i)\.useMemo.+?)/, replace: ";$1=$2.useMemo(()=>[...$self.getBadges(arguments[0].displayProfile),...$1],[$1])" diff --git a/src/plugins/_api/memberListDecorators/index.tsx b/src/plugins/_api/memberListDecorators/index.tsx index 365c21f3..90f09d8f 100644 --- a/src/plugins/_api/memberListDecorators/index.tsx +++ b/src/plugins/_api/memberListDecorators/index.tsx @@ -32,21 +32,17 @@ export default definePlugin({ { find: ".lostPermission)", replacement: [ - { - match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/, - replace: "$&vencordProps=$1," - }, { match: /children:\[(?=.{0,300},lostPermissionTooltipText:)/, - replace: "children:[(typeof vencordProps!=='undefined'&&Vencord.Api.MemberListDecorators.__getDecorators(vencordProps))," + replace: "children:[Vencord.Api.MemberListDecorators.__getDecorators(arguments[0],'guild')," } ] }, { find: "PrivateChannel.renderAvatar", replacement: { - match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/, - replace: "decorators:[Vencord.Api.MemberListDecorators.__getDecorators(arguments[0]),$1?$2:null]" + match: /decorators:(\i\.isSystemDM\(\)\?.+?:null)/, + replace: "decorators:[Vencord.Api.MemberListDecorators.__getDecorators(arguments[0],'dm'),$1]" } } ] diff --git a/src/plugins/_api/serverList.ts b/src/plugins/_api/serverList.ts index dfd40de7..89c40796 100644 --- a/src/plugins/_api/serverList.ts +++ b/src/plugins/_api/serverList.ts @@ -32,9 +32,9 @@ export default definePlugin({ } }, { - find: "#{intl::SERVERS}),children", + find: ".setGuildsTree(", replacement: { - match: /(?<=#{intl::SERVERS}\),children:)\i\.map\(\i\)/, + match: /(?<=#{intl::SERVERS}\),gap:"xs",children:)\i\.map\(.{0,50}\.length\)/, replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" } } diff --git a/src/plugins/accountPanelServerProfile/index.tsx b/src/plugins/accountPanelServerProfile/index.tsx index 8b561581..ad63ba27 100644 --- a/src/plugins/accountPanelServerProfile/index.tsx +++ b/src/plugins/accountPanelServerProfile/index.tsx @@ -9,7 +9,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { getCurrentChannel } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findComponentByCodeLazy } from "@webpack"; import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common"; import { User } from "discord-types/general"; @@ -19,8 +19,7 @@ interface UserProfileProps { originalRenderPopout: () => React.ReactNode; } -const UserProfile = findComponentByCodeLazy("UserProfilePopoutWrapper: user cannot be undefined"); -const styles = findByPropsLazy("accountProfilePopoutWrapper"); +const UserProfile = findComponentByCodeLazy(".POPOUT,user"); let openAlternatePopout = false; let accountPanelRef: React.RefObject | null> = { current: null }; @@ -77,7 +76,7 @@ export default definePlugin({ replace: "$self.useAccountPanelRef();$&" }, { - match: /(\.AVATAR,children:.+?renderPopout:(\(\i,\i\))=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/, + match: /(\.AVATAR,children:.+?renderPopout:\((\i),\i\)=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/, replace: (_, rest, popoutProps, originalPopout, currentUser) => `${rest}$self.UserProfile({popoutProps:${popoutProps},currentUser:${currentUser},originalRenderPopout:()=>{${originalPopout}}})` }, { @@ -121,14 +120,18 @@ export default definePlugin({ } const currentChannel = getCurrentChannel(); - if (currentChannel?.getGuildId() == null) { + if (currentChannel?.getGuildId() == null || !UserProfile.$$vencordGetWrappedComponent()) { return originalRenderPopout(); } return ( -
- -
+ ); }, { noop: true }) }); diff --git a/src/plugins/forceOwnerCrown/index.ts b/src/plugins/forceOwnerCrown/index.ts index 907d9dc0..bf115c64 100644 --- a/src/plugins/forceOwnerCrown/index.ts +++ b/src/plugins/forceOwnerCrown/index.ts @@ -29,8 +29,8 @@ export default definePlugin({ { find: "#{intl::GUILD_OWNER}),children:", replacement: { - match: /,isOwner:(\i),/, - replace: ",_isOwner:$1=$self.isGuildOwner(e)," + match: /(?<=decorators:.{0,200}?isOwner:)\i/, + replace: "$self.isGuildOwner(arguments[0])" } } ], diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index 0f4016ad..07723bcc 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -24,7 +24,7 @@ export default definePlugin({ patches: [ // DM User Sidebar { - find: ".PANEL}),nicknameIcons", + find: ".SIDEBAR}),nicknameIcons", replacement: { match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id)}\)}\)/, replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:true})" diff --git a/src/plugins/ircColors/index.ts b/src/plugins/ircColors/index.ts index 3e0515f5..d4f453a7 100644 --- a/src/plugins/ircColors/index.ts +++ b/src/plugins/ircColors/index.ts @@ -66,8 +66,8 @@ export default definePlugin({ { find: '="SYSTEM_TAG"', replacement: { - match: /\i.gradientClassName]\),style:/, - replace: "$&{color:$self.calculateNameColorForMessageContext(arguments[0])},_style:" + match: /(?<=\.username.{0,50}?)style:/, + replace: "style:{color:$self.calculateNameColorForMessageContext(arguments[0])},_style:" } }, { diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 1c9ad40e..d71fd933 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +import "./style.css"; + import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { isNonNullish } from "@utils/guards"; @@ -95,6 +97,12 @@ export default definePlugin({ { match: /\(0,\i\.jsx\)\(\i,\{items:\i,section:(\i)/, replace: "$1==='MUTUAL_GDMS'?$self.renderMutualGDMs(arguments[0]):$&" + }, + // Discord adds spacing between each item which pushes our tab off screen. + // set the gap to zero to ensure ours stays on screen + { + match: /className:\i\.tabBar/, + replace: "$& + ' vc-mutual-gdms-tab-bar'" } ] }, diff --git a/src/plugins/mutualGroupDMs/style.css b/src/plugins/mutualGroupDMs/style.css new file mode 100644 index 00000000..14ea83c9 --- /dev/null +++ b/src/plugins/mutualGroupDMs/style.css @@ -0,0 +1,4 @@ +.vc-mutual-gdms-tab-bar { + gap: 0; + justify-content: space-between; +} diff --git a/src/plugins/noUnblockToJump/index.ts b/src/plugins/noUnblockToJump/index.ts index 04ddf2ed..4dbbe462 100644 --- a/src/plugins/noUnblockToJump/index.ts +++ b/src/plugins/noUnblockToJump/index.ts @@ -30,7 +30,7 @@ export default definePlugin({ find: '.id,"Search Results"', replacement: [ { - match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/, + match: /if\(.{1,40}\)(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/, replace: "if(false)$1" }, { From 5195317cc472991009490e8ec7a7ac73c57e012a Mon Sep 17 00:00:00 2001 From: Eazvy <57739965+Eazvy@users.noreply.github.com> Date: Wed, 23 Apr 2025 14:39:49 -0400 Subject: [PATCH 055/198] add (noOnboarding) / update (randomVoice) (#242) * Update index.tsx * add noOnboarding, update (randomVoice) / autoStream auto-completes onboarding, upon joining a server. update (randomVoice) to have autoStream and leave when vc is empty * Silent Fail --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- src/equicordplugins/noOnboarding/index.tsx | 56 +++++++++++ src/equicordplugins/randomVoice/index.tsx | 105 ++++++++++++++++++--- 2 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 src/equicordplugins/noOnboarding/index.tsx diff --git a/src/equicordplugins/noOnboarding/index.tsx b/src/equicordplugins/noOnboarding/index.tsx new file mode 100644 index 00000000..dc29f23e --- /dev/null +++ b/src/equicordplugins/noOnboarding/index.tsx @@ -0,0 +1,56 @@ +import definePlugin from "@utils/types"; +import { Devs, EquicordDevs } from "@utils/constants"; +import { RestAPI } from "@webpack/common"; + +export default definePlugin({ + name: "NoOnboarding", + description: "Bypasses Discord's onboarding process for quicker server entry.", + authors: [EquicordDevs.omaw, Devs.Glitch], + patches: [ + { + find: ",acceptInvite(", + replacement: { + match: /INVITE_ACCEPT_SUCCESS.+?,(\i)=null!=.+?;/, + replace: (m, guildId) => `${m}$self.bypassOnboard(${guildId});` + } + }, + { + find: "{joinGuild:", + replacement: { + match: /guildId:(\i),lurker:(\i).{0,20}}\)\);/, + replace: (m, guildId, lurker) => `${m}if(!${lurker})$self.bypassOnboard(${guildId});` + } + } + ], + bypassOnboard(guild_id: string) { + RestAPI.get({ url: `/guilds/${guild_id}/onboarding` }).then(res => { + const data = res.body; + if (!data?.prompts?.length) return; + + const now = Math.floor(Date.now() / 1000); + const prompts_seen: Record = {}; + const responses_seen: Record = {}; + const responses: string[] = []; + + for (const prompt of data.prompts) { + const options = prompt.options || []; + if (!options.length) continue; + prompts_seen[prompt.id] = now; + for (const opt of options) responses_seen[opt.id] = now; + responses.push(options[options.length - 1].id); + } + + const payload = { + onboarding_responses: responses, + onboarding_prompts_seen: prompts_seen, + onboarding_responses_seen: responses_seen, + }; + + RestAPI.post({ + url: `/guilds/${guild_id}/onboarding-responses`, + body: payload + }).catch(() => {}); + }).catch(() => {}); + } + }); + diff --git a/src/equicordplugins/randomVoice/index.tsx b/src/equicordplugins/randomVoice/index.tsx index 2b5080c1..72ecae7e 100644 --- a/src/equicordplugins/randomVoice/index.tsx +++ b/src/equicordplugins/randomVoice/index.tsx @@ -11,8 +11,8 @@ import { makeRange } from "@components/PluginSettings/components"; import { debounce } from "@shared/debounce"; import { EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; -import { ChannelStore, ContextMenuApi, GuildStore, Menu, NavigationRouter, PermissionStore, React, SelectedChannelStore, Toasts, UserStore } from "@webpack/common"; +import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy, findByCode, findByProps } from "@webpack"; +import { ChannelStore, ContextMenuApi, GuildStore, IconUtils, Menu, ChannelRouter, PermissionStore, React, SelectedChannelStore, PermissionsBits, Toasts, UserStore } from "@webpack/common"; import style from "./styles.css?managed"; @@ -32,6 +32,7 @@ const valueOperation = [ const CONNECT = 1n << 20n; const SPEAK = 1n << 21n; const STREAM = 1n << 9n; +const VIDEO = 1 << 21; const settings = definePluginSettings({ UserAmountOperation: { @@ -85,6 +86,11 @@ const settings = definePluginSettings({ description: "Automatically turns on camera", default: false, }, + autoStream: { + type: OptionType.BOOLEAN, + description: "Automatically turns on stream", + default: false, + }, selfMute: { type: OptionType.BOOLEAN, description: "Automatically mutes your mic when joining voice-channel.", @@ -95,6 +101,11 @@ const settings = definePluginSettings({ description: "Automatically deafems your mic when joining voice-channel.", default: false, }, + leaveEmpty: { + type: OptionType.BOOLEAN, + description: "Finds a random-call, when the voice chat is empty.", + default: false, + }, avoidStages: { type: OptionType.BOOLEAN, description: "Avoids joining stage voice-channels.", @@ -137,11 +148,25 @@ const settings = definePluginSettings({ }, }); +interface VoiceState { + userId: string; + channelId?: string; + oldChannelId?: string; + deaf: boolean; + mute: boolean; + selfDeaf: boolean; + selfMute: boolean; + selfStream: boolean; + selfVideo: boolean; + sessionId: string; + suppress: boolean; + requestToSpeakTimestamp: string | null; +} export default definePlugin({ name: "RandomVoice", description: "Adds a Button near the Mute button to join a random voice call.", - authors: [EquicordDevs.xijexo, EquicordDevs.omaw], + authors: [EquicordDevs.xijexo, EquicordDevs.omaw, EquicordDevs.thororen], patches: [ { find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}", @@ -151,6 +176,22 @@ export default definePlugin({ } } ], + flux: { + VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[] }) { + const currentUserId = UserStore.getCurrentUser().id; + const myChannelId = VoiceStateStore.getVoiceStateForUser(currentUserId)?.channelId; + if (!myChannelId || !settings.store.leaveEmpty) return; + + const voiceStatesMap = VoiceStateStore.getVoiceStates() as Record; + const othersInChannel = Object.values(voiceStatesMap).filter(vs => + vs.channelId === myChannelId && vs.userId !== currentUserId + ); + + if (othersInChannel.length === 0) { + randomVoice() + } + }, + }, start() { enableStyle(style); }, @@ -194,8 +235,7 @@ function ContextMenu() { }); ServerList = Array.from(new Set(ServerList)); - const Servers = ServerList.map(server => GuildStore.getGuild(server)).filter(guild => guild !== null); - + const Servers = ServerList.map(server => GuildStore.getGuild(server)).filter(guild => guild && guild.id); const [servers, setServers] = React.useState(settings.store.Servers); const [SpacesLeftOperation, setSpacesLeftOperation] = React.useState(settings.store.spacesLeftOperation); const [userAmount, setuserAmount] = React.useState(settings.store.UserAmountOperation); @@ -204,12 +244,13 @@ function ContextMenu() { const [stage, setStage] = React.useState(settings.store.avoidStages); const [afk, setAfk] = React.useState(settings.store.avoidAfk); const [camera, setCamera] = React.useState(settings.store.autoCamera); + const [stream, setStream] = React.useState(settings.store.autoStream); + const [empty, setEmpty] = React.useState(settings.store.leaveEmpty); const [muteself, setSelfMute] = React.useState(settings.store.selfMute); const [deafenself, setSelfDeafen] = React.useState(settings.store.selfDeafen); const [mute, setMute] = React.useState(settings.store.mute); const [deafen, setDeafen] = React.useState(settings.store.deafen); const [video, setVideo] = React.useState(settings.store.video); - const [stream, setStream] = React.useState(settings.store.stream); const [state, setState] = React.useState(settings.store.includeStates); const [notstate, avoidState] = React.useState(settings.store.avoidStates); @@ -219,6 +260,8 @@ function ContextMenu() { onClose={() => { }} aria-label="Voice state modifier" > + + { setVideo(!video); settings.store.video = !video; @@ -545,7 +588,7 @@ function ContextMenu() { { }} > <> @@ -577,6 +620,24 @@ function ContextMenu() { settings.store.autoCamera = !camera; }} checked={camera} /> + { + setStream(!stream); + settings.store.autoStream = !stream; + }} + checked={stream} /> + { + setEmpty(!empty); + settings.store.leaveEmpty = !empty; + }} + checked={empty} /> @@ -704,15 +765,35 @@ function getChannels() { function JoinVc(channelID) { const channel = ChannelStore.getChannel(channelID); - const channel_link = `/channels/${channel.guild_id}/${channel.id}`; ChannelActions.selectVoiceChannel(channelID); - if (settings.store.autoNavigate) NavigationRouter.transitionTo(channel_link); - if (settings.store.autoCamera && PermissionStore.can(STREAM, channel)) autoCamera(); - if (settings.store.autoCamera && PermissionStore.can(STREAM, channel)) autoCamera(); + if (settings.store.autoNavigate) ChannelRouter.transitionToChannel(channel.id); + if (settings.store.autoCamera && PermissionStore.can(VIDEO, channel)) autoCamera(); + if (settings.store.autoStream && PermissionStore.can(STREAM, channel)) autoStream(); if (settings.store.selfMute && !MediaEngineStore.isSelfMute() && SelectedChannelStore.getVoiceChannelId()) toggleSelfMute(); if (settings.store.selfDeafen && !MediaEngineStore.isSelfDeaf() && SelectedChannelStore.getVoiceChannelId()) toggleSelfDeaf(); } +async function autoStream() { + const startStream = findByCode('type:"STREAM_START"'); + const mediaEngine = findByProps("getMediaEngine").getMediaEngine(); + const getDesktopSources = findByCode("desktop sources"); + const selected = SelectedChannelStore.getVoiceChannelId(); + if (!selected) return; + const channel = ChannelStore.getChannel(selected); + const sources = await getDesktopSources(mediaEngine, ["screen"], null); + if (!sources || sources.length === 0) return; + const source = sources[0]; + if (channel.type === 13 || !PermissionStore.can(PermissionsBits.STREAM, channel)) return; + startStream(channel.guild_id, selected, { + "pid": null, + "sourceId": source.id, + "sourceName": source.name, + "audioSourceId": null, + "sound": true, + "previewDisabled": false + }); +} + function autoCamera() { const checkExist = setInterval(() => { const cameraOFF = document.querySelector('[aria-label="Turn off Camera" i]') as HTMLButtonElement; From 557fdcfc6020292fec1a2aca0889b95231c01aa1 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 23 Apr 2025 18:01:06 -0400 Subject: [PATCH 056/198] Fix Formatting --- README.md | 3 +- src/equicordplugins/noOnboarding/index.tsx | 100 +++++++++++---------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 0562fef0..428f17c7 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - NoMirroredCamera by Nyx - NoModalAnimation by AutumnVN - NoNitroUpsell by thororen +- NoOnboarding by omaw & Glitch - NoRoleHeaders by Samwich - NotificationTitle by Kyuuhachi - OnePingPerDM by ProffDea @@ -126,7 +127,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - QuestCompleter by Amia - QuestionMarkReplacement by nyx - Quoter by Samwich -- RandomVoice by xijexo & omaw +- RandomVoice by xijexo, omaw, thororen - Remix by MrDiamond - RemixMe by kvba - RepeatMessage by Tolgchu diff --git a/src/equicordplugins/noOnboarding/index.tsx b/src/equicordplugins/noOnboarding/index.tsx index dc29f23e..f01c7127 100644 --- a/src/equicordplugins/noOnboarding/index.tsx +++ b/src/equicordplugins/noOnboarding/index.tsx @@ -1,56 +1,62 @@ -import definePlugin from "@utils/types"; +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + import { Devs, EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; import { RestAPI } from "@webpack/common"; export default definePlugin({ name: "NoOnboarding", description: "Bypasses Discord's onboarding process for quicker server entry.", authors: [EquicordDevs.omaw, Devs.Glitch], - patches: [ - { - find: ",acceptInvite(", - replacement: { - match: /INVITE_ACCEPT_SUCCESS.+?,(\i)=null!=.+?;/, - replace: (m, guildId) => `${m}$self.bypassOnboard(${guildId});` - } - }, - { - find: "{joinGuild:", - replacement: { - match: /guildId:(\i),lurker:(\i).{0,20}}\)\);/, - replace: (m, guildId, lurker) => `${m}if(!${lurker})$self.bypassOnboard(${guildId});` - } + patches: [ + { + find: ",acceptInvite(", + replacement: { + match: /INVITE_ACCEPT_SUCCESS.+?,(\i)=null!=.+?;/, + replace: (m, guildId) => `${m}$self.bypassOnboard(${guildId});` } - ], - bypassOnboard(guild_id: string) { - RestAPI.get({ url: `/guilds/${guild_id}/onboarding` }).then(res => { - const data = res.body; - if (!data?.prompts?.length) return; - - const now = Math.floor(Date.now() / 1000); - const prompts_seen: Record = {}; - const responses_seen: Record = {}; - const responses: string[] = []; - - for (const prompt of data.prompts) { - const options = prompt.options || []; - if (!options.length) continue; - prompts_seen[prompt.id] = now; - for (const opt of options) responses_seen[opt.id] = now; - responses.push(options[options.length - 1].id); - } - - const payload = { - onboarding_responses: responses, - onboarding_prompts_seen: prompts_seen, - onboarding_responses_seen: responses_seen, - }; - - RestAPI.post({ - url: `/guilds/${guild_id}/onboarding-responses`, - body: payload - }).catch(() => {}); - }).catch(() => {}); - } - }); + }, + { + find: "{joinGuild:", + replacement: { + match: /guildId:(\i),lurker:(\i).{0,20}}\)\);/, + replace: (m, guildId, lurker) => `${m}if(!${lurker})$self.bypassOnboard(${guildId});` + } + } + ], + bypassOnboard(guild_id: string) { + RestAPI.get({ url: `/guilds/${guild_id}/onboarding` }).then(res => { + const data = res.body; + if (!data?.prompts?.length) return; + + const now = Math.floor(Date.now() / 1000); + const prompts_seen: Record = {}; + const responses_seen: Record = {}; + const responses: string[] = []; + + for (const prompt of data.prompts) { + const options = prompt.options || []; + if (!options.length) continue; + prompts_seen[prompt.id] = now; + for (const opt of options) responses_seen[opt.id] = now; + responses.push(options[options.length - 1].id); + } + + const payload = { + onboarding_responses: responses, + onboarding_prompts_seen: prompts_seen, + onboarding_responses_seen: responses_seen, + }; + + RestAPI.post({ + url: `/guilds/${guild_id}/onboarding-responses`, + body: payload + }).catch(() => { }); + }).catch(() => { }); + } +}); From f5c099c4a41ccc099c0097e5f90c16f4ba233cec Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 23 Apr 2025 18:27:51 -0400 Subject: [PATCH 057/198] Fix CustomUserColors --- src/equicordplugins/customUserColors/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index b75d8659..034b6187 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -98,8 +98,8 @@ export default definePlugin({ // this also affects name headers in chats outside of servers find: '="SYSTEM_TAG"', replacement: { - match: /\i.gradientClassName]\),style:/, - replace: "$&{color:$self.colorIfServer(arguments[0])},_style:" + match: /(?<=\.username.{0,50}?)style:/, + replace: "style:{color:$self.colorIfServer(arguments[0])},_style:" }, predicate: () => !Settings.plugins.IrcColors.enabled }, From e9212fc73ddb0e6d108e8fff68d3fec318d091ce Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 23 Apr 2025 18:36:33 -0400 Subject: [PATCH 058/198] "Fix Lint" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 428f17c7..3b4e52f9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-167 additional plugins +168 additional plugins ### All Platforms From aef162d3b5f324e649f48e08bcdc2eb806d3516a Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 23 Apr 2025 18:50:19 -0400 Subject: [PATCH 059/198] Fix QuestCompleter Alignment --- src/equicordplugins/questCompleter.discordDesktop/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/questCompleter.discordDesktop/style.css b/src/equicordplugins/questCompleter.discordDesktop/style.css index ecdccebb..cccdaa87 100644 --- a/src/equicordplugins/questCompleter.discordDesktop/style.css +++ b/src/equicordplugins/questCompleter.discordDesktop/style.css @@ -1,5 +1,5 @@ -.vc-quest-completer-icon{ - bottom: -4px; +.vc-quest-completer-icon { + bottom: -2px; padding: 0; width: var(--space-32); min-width: 0; From 700b971e7dced02e4b5995e1cb0179897607af35 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 25 Apr 2025 00:45:17 +0200 Subject: [PATCH 060/198] MutualGroupDMs: fix display in dm profile sidebar --- src/plugins/mutualGroupDMs/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index d71fd933..3c8e30fe 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -32,7 +32,7 @@ const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); const UserUtils = findByPropsLazy("getGlobalName"); const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds"); -const ExpandableList = findComponentByCodeLazy('"PRESS_SECTION"'); +const ExpandableList = findComponentByCodeLazy('"PRESS_SECTION"', ".header"); const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon"); function getGroupDMName(channel: Channel) { From 3338c1067863ed3dc23331bd485c456af11d76fa Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:32:44 -0400 Subject: [PATCH 061/198] OnePingPerDM Moved To Vencord --- README.md | 3 +-- src/{equicordplugins => plugins}/onePingPerDM/index.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) rename src/{equicordplugins => plugins}/onePingPerDM/index.ts (97%) diff --git a/README.md b/README.md index 3b4e52f9..928bc36a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-168 additional plugins +167 additional plugins ### All Platforms @@ -118,7 +118,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - NoOnboarding by omaw & Glitch - NoRoleHeaders by Samwich - NotificationTitle by Kyuuhachi -- OnePingPerDM by ProffDea - PingNotifications by smuki - PinIcon by iamme - PlatformSpoofer by Drag diff --git a/src/equicordplugins/onePingPerDM/index.ts b/src/plugins/onePingPerDM/index.ts similarity index 97% rename from src/equicordplugins/onePingPerDM/index.ts rename to src/plugins/onePingPerDM/index.ts index 5fc0778d..e9cde652 100644 --- a/src/equicordplugins/onePingPerDM/index.ts +++ b/src/plugins/onePingPerDM/index.ts @@ -49,7 +49,7 @@ export default definePlugin({ replace: "$&if(!$self.isPrivateChannelRead(arguments[0]?.message))return;else " }, { - match: /sound:(\i\?\i.{0,30},onClick)/, + match: /sound:(\i\?\i:void 0,soundpack:\i,volume:\i,onClick)/, replace: "sound:!$self.isPrivateChannelRead(arguments[0]?.message)?undefined:$1" }] }], From 5e6567e1c56dffedcde98bd2eb6e6eb76ca9f6a6 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:23:15 -0400 Subject: [PATCH 062/198] Fix More Stickers --- src/equicordplugins/moreStickers/index.tsx | 54 +++++++--------------- src/equicordplugins/moreStickers/style.css | 2 +- 2 files changed, 18 insertions(+), 38 deletions(-) diff --git a/src/equicordplugins/moreStickers/index.tsx b/src/equicordplugins/moreStickers/index.tsx index 2388329f..08796396 100644 --- a/src/equicordplugins/moreStickers/index.tsx +++ b/src/equicordplugins/moreStickers/index.tsx @@ -35,54 +35,34 @@ export default definePlugin({ find: "ChannelStickerPickerButton", replacement: [{ match: /(children:\(0,\i\.jsx\)\()(.{0,10})({innerClassName.{10,30}\.stickerButton)/, - replace: (_, head, button, tail) => { - const isMoreStickers = "arguments[0]?.stickersType"; - return `${head}${isMoreStickers}?$self.stickerButton:${button}${tail}`; - } + replace: "$1arguments[0]?.stickersType?$self.stickerButton:$2$3" }, { - match: /(\i=)(\i\.useCallback.{0,25}\.STICKER,.{0,10});/, - replace: (_, decl, cb) => { - const newCb = cb.replace(/(?<=\(\)=>\{\(.*?\)\().+?\.STICKER/, "\"stickers+\""); - return `${decl}arguments[0]?.stickersType?${newCb}:${cb};`; - } + match: /(\i=)((\i\.useCallback\(\(\)=>\{\(.*?\)\().*?\.STICKER,(\i.{0,10}));/, + replace: '$1arguments[0]?.stickersType?$3"stickers+",$4:$2;' }, { match: /(\i)=((\i)===\i\.\i\.STICKER)/, - replace: (_, isActive, isStickerTab, currentTab) => { - const c = "arguments[0].stickersType"; - return `${isActive}=${c}?(${currentTab}===${c}):(${isStickerTab})`; - } + replace: "$1=arguments[0].stickersType?($3===arguments[0].stickersType):($2)" }] }, { find: ".gifts)", replacement: { - match: /,.{0,5}\(null==\(\i=\i\.stickers\)\?void 0.*?(\i)\.push\((\(0,\w\.jsx\))\((.+?),{disabled:\i,type:(\i)},"sticker"\)\)\)/, - replace: (m, _, jsx, compo, type) => { - const c = "arguments[0].type"; - return `${m},${c}?.submit?.button&&${_}.push(${jsx}(${compo},{disabled:!${c}?.submit?.button,type:${type},stickersType:"stickers+"},"stickers+"))`; - } + match: /(?<=,.{0,5}\(null==\(\i=\i\.stickers\)\?void 0.*?(\i)\.push\((\(0,\i\.jsx\))\((.+?),{disabled:\i,type:(\i)},"sticker"\)\)\))/, + replace: ",arguments[0].type?.submit?.button&&$1.push($2($3,{disabled:!arguments[0].type?.submit?.button,type:$4,stickersType:\"stickers+\"},\"stickers+\"))" } }, { find: "#{intl::EXPRESSION_PICKER_CATEGORIES_A11Y_LABEL}", - replacement: { - match: /role:"tablist",.*?,?"aria-label":.+?\),children:(\[.*?\)\]}\)}\):null,)(.*?closePopout:\w.*?:null)/s, - replace: m => { - const stickerTabRegex = /(\w+?)\?(\([^()]+?\))\((.{1,2}),{.{0,128},isActive:(.{1,2})===.{1,6}\.STICKER.{1,140},children:(.{1,5}\.string\(.+?\)).*?:null/s; - const res = m.replace(stickerTabRegex, (_m, canUseStickers, jsx, tabHeaderComp, currentTab, stickerText) => { - const isActive = `${currentTab}==="stickers+"`; - return ( - `${_m},${canUseStickers}?` + - `${jsx}(${tabHeaderComp},{id:"stickers+-picker-tab","aria-controls":"more-stickers-picker-tab-panel","aria-selected":${isActive},isActive:${isActive},autoFocus:true,viewType:"stickers+",children:${jsx}("div",{children:${stickerText}+"+"})})` + - ":null" - ); - }); - - return res.replace(/:null,((.{1,200})===.{1,30}\.STICKER&&\w+\?(\([^()]{1,10}\)).{1,15}?(\{.*?,onSelectSticker:.*?\})\):null)/s, (_, _m, currentTab, jsx, props) => { - return `:null,${currentTab}==="stickers+"?${jsx}($self.moreStickersComponent,${props}):null,${_m}`; - }); + replacement: [ + { + match: /(?<=null,(\i)\?(\(.*?\))\((\i),{.{0,128},isActive:(\i)===.{0,200},children:(\i\.intl\.string\(.*?\))\}\)\}\):null,)/s, + replace: '$1?$2($3,{id:"stickers+-picker-tab","aria-controls":"more-stickers-picker-tab-panel","aria-selected":$4==="stickers+",isActive:$4==="stickers+",autoFocus:true,viewType:"stickers+",children:$5+"+"}):null,' + }, + { + match: /:null,((.{1,200})===.{1,30}\.STICKER&&\w+\?(\([^()]{1,10}\)).{1,15}?(\{.*?,onSelectSticker:.*?\})\):null)/, + replace: ':null,$2==="stickers+"?$3($self.moreStickersComponent,$4):null,$1' } - } + ] }, { find: '==="remove_text"', @@ -114,8 +94,8 @@ export default definePlugin({ > - - + + ); }, moreStickersComponent({ diff --git a/src/equicordplugins/moreStickers/style.css b/src/equicordplugins/moreStickers/style.css index 6392044a..16101bf4 100644 --- a/src/equicordplugins/moreStickers/style.css +++ b/src/equicordplugins/moreStickers/style.css @@ -116,7 +116,7 @@ .vc-more-stickers-category-scroller, .vc-more-stickers-picker-content-scroller { - scrollbar-width: thin; + scrollbar-width: none; scrollbar-color: var(--scrollbar-thin-thumb) var(--scrollbar-thin-track); } From fe3777b1b014080f2f6518e2f32522b106648b67 Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:52:29 -0400 Subject: [PATCH 063/198] update companion (#243) --- src/plugins/devCompanion.dev/index.tsx | 5 +++-- src/plugins/devCompanion.dev/initWs.tsx | 17 +++++++++++++++-- src/plugins/devCompanion.dev/types/index.ts | 1 - src/plugins/devCompanion.dev/types/recieve.ts | 9 ++++++++- src/plugins/devCompanion.dev/types/send.ts | 9 ++++++++- src/plugins/devCompanion.dev/util.tsx | 3 ++- 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 50afbb0f..23d4c6a3 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -61,9 +61,10 @@ export default definePlugin({ start() { // if we're running the reporter, we need to initws in the reporter file to avoid a race condition - if (!IS_COMPANION_TEST) - initWs(); + if (!IS_DEV) throw new Error("This plugin requires dev mode to run, please build with pnpm build --dev"); + initWs(); }, stop: stopWs, }); + diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx index 36ee4778..c264338e 100644 --- a/src/plugins/devCompanion.dev/initWs.tsx +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -6,6 +6,7 @@ import { popNotice, showNotice } from "@api/Notices"; import ErrorBoundary from "@components/ErrorBoundary"; +import { getIntlMessageFromHash } from "@utils/discord"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import { filters, findAll, search, wreq } from "@webpack"; import { React, Toasts, useState } from "@webpack/common"; @@ -125,7 +126,7 @@ export function initWs(isManual = false) { function reply(error?: string) { const toSend = { nonce: d.nonce, ok: !error } as Record; if (error) toSend.error = error; - logger.debug("Replying with:", toSend); + logger.debug(`Replying with:`, toSend); ws.send(JSON.stringify(toSend)); } function replyData(data: OutgoingMessage) { @@ -437,8 +438,20 @@ export function initWs(isManual = false) { }); break; } + case "i18n": { + const { hashedKey } = d.data; + replyData({ + type: "i18n", + ok: true, + data: { + value: getIntlMessageFromHash(hashedKey) + } + }); + break; + } default: - reply("Unknown Type " + (d as any).type); + // @ts-expect-error should be never + reply("Unknown Type " + d?.type); break; } }); diff --git a/src/plugins/devCompanion.dev/types/index.ts b/src/plugins/devCompanion.dev/types/index.ts index 532843e1..18bc6854 100644 --- a/src/plugins/devCompanion.dev/types/index.ts +++ b/src/plugins/devCompanion.dev/types/index.ts @@ -5,4 +5,3 @@ */ export * as Recieve from "./recieve"; -export * as Send from "./send"; diff --git a/src/plugins/devCompanion.dev/types/recieve.ts b/src/plugins/devCompanion.dev/types/recieve.ts index f14c5db2..8539461a 100644 --- a/src/plugins/devCompanion.dev/types/recieve.ts +++ b/src/plugins/devCompanion.dev/types/recieve.ts @@ -62,7 +62,7 @@ export type FindData = { args: FindNode[]; }; -export type IncomingMessage = DisablePlugin | RawId | DiffPatch | Reload | ExtractModule | TestPatch | TestFind | AllModules; +export type IncomingMessage = DisablePlugin | RawId | DiffPatch | Reload | ExtractModule | TestPatch | TestFind | AllModules | I18nLookup; export type FullIncomingMessage = IncomingMessage & { nonce: number; }; export type DisablePlugin = { @@ -73,6 +73,13 @@ export type DisablePlugin = { }; }; +export type I18nLookup = { + type: "i18n"; + data: { + hashedKey: string; + }; +}; + /** * @deprecated use {@link ExtractModule} instead */ diff --git a/src/plugins/devCompanion.dev/types/send.ts b/src/plugins/devCompanion.dev/types/send.ts index 0f1594f3..9daf1418 100644 --- a/src/plugins/devCompanion.dev/types/send.ts +++ b/src/plugins/devCompanion.dev/types/send.ts @@ -8,7 +8,7 @@ import { ReporterData as IReporterData } from "debug/reporterData"; export type ReporterData = IReporterData; -export type OutgoingMessage = (Report | DiffModule | ExtractModule | ModuleList | RawId) & Base; +export type OutgoingMessage = (Report | DiffModule | ExtractModule | ModuleList | RawId | I18nValue) & Base; export type FullOutgoingMessage = OutgoingMessage & Nonce; export type Base = { @@ -34,6 +34,13 @@ export type ModuleResult = { }; // #region valid payloads +export type I18nValue = { + type: "i18n"; + data: { + value: string; + }; +}; + export type Report = { type: "report"; data: ReporterData; diff --git a/src/plugins/devCompanion.dev/util.tsx b/src/plugins/devCompanion.dev/util.tsx index ca6304ed..436856f1 100644 --- a/src/plugins/devCompanion.dev/util.tsx +++ b/src/plugins/devCompanion.dev/util.tsx @@ -9,11 +9,12 @@ import { Settings } from "@api/Settings"; import { canonicalizeMatch } from "@utils/patches"; import { CodeFilter, stringMatches, wreq } from "@webpack"; import { Toasts } from "@webpack/common"; +import { WebpackPatcher } from "Vencord"; import { logger, settings as companionSettings } from "."; import { FindNode } from "./types/recieve"; -const { WebpackPatcher: { getFactoryPatchedBy, getFactoryPatchedSource } } = require("Vencord") as typeof import("Vencord"); +const { getFactoryPatchedBy, getFactoryPatchedSource } = WebpackPatcher; /** * extracts the patched module, if there is no patched module, throws an error From 78dce83962ad0756b27f50412e49171d34937089 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:29:19 -0400 Subject: [PATCH 064/198] Fix Quoter URL --- src/equicordplugins/quoter/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/quoter/index.tsx b/src/equicordplugins/quoter/index.tsx index d2120bfb..2c311bbe 100644 --- a/src/equicordplugins/quoter/index.tsx +++ b/src/equicordplugins/quoter/index.tsx @@ -141,7 +141,7 @@ async function createQuoteImage(avatarUrl: string, quoteOld: string, grayScale: ctx.fillRect(0, 0, canvas.width, canvas.height); const avatarBlob = await fetchImageAsBlob(avatarUrl); - const fadeBlob = await fetchImageAsBlob("https://github.com/Equicord/Equibored/raw/main/misc/quoter.png"); + const fadeBlob = await fetchImageAsBlob("https://github.com/Equicord/Equibored/raw/main/icons/quoter/quoter.png"); const avatar = new Image(); const fade = new Image(); From db08d793143d6b8a86a7fdf933e37ea829eadf8b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:32:09 -0400 Subject: [PATCH 065/198] =?UTF-8?q?Make=20it=20actually=20go=20to=20the=20?= =?UTF-8?q?image=20=F0=9F=98=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/equicordplugins/quoter/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/quoter/index.tsx b/src/equicordplugins/quoter/index.tsx index 2c311bbe..c15cf399 100644 --- a/src/equicordplugins/quoter/index.tsx +++ b/src/equicordplugins/quoter/index.tsx @@ -141,7 +141,7 @@ async function createQuoteImage(avatarUrl: string, quoteOld: string, grayScale: ctx.fillRect(0, 0, canvas.width, canvas.height); const avatarBlob = await fetchImageAsBlob(avatarUrl); - const fadeBlob = await fetchImageAsBlob("https://github.com/Equicord/Equibored/raw/main/icons/quoter/quoter.png"); + const fadeBlob = await fetchImageAsBlob("https://raw.githubusercontent.com/Equicord/Equibored/refs/heads/main/icons/quoter/quoter.png"); const avatar = new Image(); const fade = new Image(); From 720edbdc3b4e4c8c6fcf9933d8e4b36066e37270 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 26 Apr 2025 16:19:59 -0400 Subject: [PATCH 066/198] Fixes For BetterFolders & ReviewDB Co-Authored-By: sadan4 <117494111+sadan4@users.noreply.github.com> --- src/plugins/betterFolders/index.tsx | 27 +++++++++++++-------------- src/plugins/reviewDB/index.tsx | 20 ++++++++++---------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 4143d293..1bb52944 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -106,14 +106,10 @@ export const settings = definePluginSettings({ } }); -const cssMade = false; - -const cssElementId = "VC-BetterFolders"; - export default definePlugin({ name: "BetterFolders", description: "Shows server folders on dedicated sidebar and adds folder related improvements", - authors: [Devs.juby, Devs.AutumnVN, Devs.Nuckyz, Devs.sadan], + authors: [Devs.juby, Devs.AutumnVN, Devs.Nuckyz], settings, @@ -123,26 +119,29 @@ export default definePlugin({ predicate: () => settings.store.sidebar, replacement: [ // Create the isBetterFolders variable in the GuildsBar component + // Needed because we access this from a closure so we can't use arguments[0] { - match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i,)/g, + match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i,)/, replace: "$&,isBetterFolders" }, + // Discord extacts the folders component, we need to pass the isBetterFolders and betterFoldersExpandedIds variable to it + { + match: /0,\i\.jsxs?[^0}]{0,100}guildDiscoveryButton:\i,/g, + replace: "$&isBetterFolders:arguments[0]?.isBetterFolders,betterFoldersExpandedIds:arguments[0]?.betterFoldersExpandedIds," + }, // If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders { - match: /\[(\i)\]=(\(0,\i\.\i\).{0,40}getGuildsTree\(\).+?}\))(?=,)/g, + match: /\[(\i)\]=(\(0,\i\.\i\).{0,40}getGuildsTree\(\).+?}\))(?=,)/, replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0]?.isBetterFolders,betterFoldersOriginalTree,arguments[0]?.betterFoldersExpandedIds)` }, // If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children { - match: /lastTargetNode:\i\[\i\.length-1\].+?}\)(?::null)?\](?=}\))/g, + match: /lastTargetNode:\i\[\i\.length-1\].+?}\)(?::null)?\](?=}\))/, replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0]?.isBetterFolders))" }, // If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children - // As of now, this is just the unread indicator at the bottom - // Discord has two different sidebars controlled by an experiment - // only the second one needs this filter { - match: /topSection.+?unreadMentionsIndicatorBottom,.+?}\)\]/, + match: /lurkingGuildIds:\i\}\)\](?=\}\)\})/, replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0]?.isBetterFolders))" }, // With one of the sidebar versions, there is a sticky top bar. Don't render it if we are rendering the Better Folders sidebar @@ -159,7 +158,7 @@ export default definePlugin({ }, // Export the isBetterFolders variable to the folders component { - match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/g, + match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/, replace: '$&isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,' } ] @@ -342,7 +341,7 @@ export default definePlugin({ return child => { if (!isBetterFolders) return true; - return child?.props?.className?.includes("itemsContainer") && child.props.children != null; + return !!child?.props?.renderTreeNode; }; }, diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index 858b82f3..7a61821b 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -77,25 +77,25 @@ export default definePlugin({ patches: [ { - find: ".BITE_SIZE,user:", + find: ".POPOUT,user:", replacement: { - match: /{profileType:\i\.\i\.BITE_SIZE,children:\[/, - replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," + match: /(children:\[)([^[]+shouldShowTooltip:)/, + replace: "$1$self.BiteSizeReviewsButton({user:arguments[0].user}),$2" } }, { - find: ".FULL_SIZE,user:", + find: ".MODAL,user:", replacement: { - match: /{profileType:\i\.\i\.FULL_SIZE,children:\[/, - replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," + match: /(children:\[)([^[]+shouldShowTooltip:)/, + replace: "$1$self.BiteSizeReviewsButton({user:arguments[0].user}),$2" } }, { - // location: "UserProfileSiebar" - find: ".PANEL,children:[", + // places like the user profile on the right in dms + find: 'location:"UserProfileSiebar"', replacement: { - match: /{profileType:\i\.\i\.PANEL,children:\[/, - replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," + match: /(children:\[)([^[]+shouldShowTooltip:)/, + replace: "$1$self.BiteSizeReviewsButton({user:arguments[0].user}),$2" } } ], From 3d1c654c20808eb794a5a89bca6ade0e90a4f2a9 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 27 Apr 2025 13:40:42 -0400 Subject: [PATCH 067/198] ToastNotifications --- README.md | 3 +- .../components/NotificationComponent.tsx | 144 +++++ .../components/Notifications.tsx | 107 ++++ .../toastNotifications/components/styles.css | 146 +++++ .../toastNotifications/index.tsx | 583 ++++++++++++++++++ .../toastNotifications/types.ts | 39 ++ src/utils/constants.ts | 8 + 7 files changed, 1029 insertions(+), 1 deletion(-) create mode 100644 src/equicordplugins/toastNotifications/components/NotificationComponent.tsx create mode 100644 src/equicordplugins/toastNotifications/components/Notifications.tsx create mode 100644 src/equicordplugins/toastNotifications/components/styles.css create mode 100644 src/equicordplugins/toastNotifications/index.tsx create mode 100644 src/equicordplugins/toastNotifications/types.ts diff --git a/README.md b/README.md index 928bc36a..3b3b6071 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-167 additional plugins +168 additional plugins ### All Platforms @@ -152,6 +152,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - ThemeLibrary by Fafa - Timezones by Aria - Title by Kyuuhachi +- ToastNotifications by Skully, Ethan, Buzzy - ToggleVideoBind by mochie - TosuRPC by AutumnVN - Translate+ by Prince527 & Ven diff --git a/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx b/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx new file mode 100644 index 00000000..17e5c4ad --- /dev/null +++ b/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx @@ -0,0 +1,144 @@ +/* + * 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 . +*/ + +import "./styles.css"; + +import ErrorBoundary from "@components/ErrorBoundary"; +import { classes } from "@utils/misc"; +import { React, useEffect, useMemo, useState } from "@webpack/common"; + +import { settings as PluginSettings } from "../index"; +import { NotificationData } from "./Notifications"; + +export default ErrorBoundary.wrap(function NotificationComponent({ + title, + body, + richBody, + icon, + image, + permanent, + dismissOnClick, + index, + onClick, + onClose, + attachments +}: NotificationData & { index?: number; }) { + const [isHover, setIsHover] = useState(false); + const [elapsed, setElapsed] = useState(0); + + let renderBody: boolean = true; + let footer: boolean = false; + + if (attachments > 0) + footer = true; + + if (body === "") + renderBody = false; + + // Precompute appearance settings. + const AppearanceSettings = { + position: `toastnotifications-position-${PluginSettings.store.position || "bottom-left"}`, + timeout: (PluginSettings.store.timeout * 1000) || 5000, + opacity: PluginSettings.store.opacity / 100, + }; + + const start = useMemo(() => Date.now(), [isHover]); // Reset the timer when the user hovers over the notification. + + // Precompute the position style. + const positionStyle = useMemo(() => { + if (index === undefined) return {}; + const isTopPosition = AppearanceSettings.position.includes("top"); + const actualHeight = 115; // Update this with the actual height including margin + const effectiveIndex = index % PluginSettings.store.maxNotifications; + const offset = 10 + (effectiveIndex * actualHeight); // 10 is the base offset + + return isTopPosition ? { top: `${offset}px` } : { bottom: `${offset}px` }; + }, [index, AppearanceSettings.position]); + + // Handle notification timeout. + useEffect(() => { + if (isHover || permanent) return void setElapsed(0); + + const intervalId = setInterval(() => { + const elapsed = Date.now() - start; + if (elapsed >= AppearanceSettings.timeout) + onClose!(); + else + setElapsed(elapsed); + }, 10); + + return () => clearInterval(intervalId); + }, [isHover]); + + const timeoutProgress = elapsed / AppearanceSettings.timeout; + + // Render the notification. + return ( + +
+
+ {renderBody ? richBody ??

{body}

: null} + {PluginSettings.store.renderImages && image && ToastNotification Image} + {footer &&

{`${attachments} attachment${attachments > 1 ? "s" : ""} ${attachments > 1 ? "were" : "was"} sent.`}

} +
+
+ + {AppearanceSettings.timeout !== 0 && !permanent && ( +
+ )} + + ); +}, { + onError: ({ props }) => props.onClose!() +}); diff --git a/src/equicordplugins/toastNotifications/components/Notifications.tsx b/src/equicordplugins/toastNotifications/components/Notifications.tsx new file mode 100644 index 00000000..fa10e2ae --- /dev/null +++ b/src/equicordplugins/toastNotifications/components/Notifications.tsx @@ -0,0 +1,107 @@ +/* + * 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 . +*/ + +import { React, ReactDOM } from "@webpack/common"; +import type { JSX, ReactNode } from "react"; +import type { Root } from "react-dom/client"; + +import { settings as PluginSettings } from "../index"; +import NotificationComponent from "./NotificationComponent"; + +let NotificationQueue: JSX.Element[] = []; +let notificationID = 0; +let RootContainer: Root; + +/** + * getNotificationContainer() + * Gets the root container for the notifications, creating it if it doesn't exist. + * @returns {Root} The root DOM container. + */ +function getNotificationContainer() { + if (!RootContainer) { + const container = document.createElement("div"); + container.id = "toastnotifications-container"; + document.body.append(container); + RootContainer = ReactDOM.createRoot(container); + } + + return RootContainer; +} + +export interface NotificationData { + title: string; // Title to display in the notification. + body: string; // Notification body text. + richBody?: ReactNode; // Same as body, though a rich ReactNode to be rendered within the notification. + icon?: string; // Avatar image of the message author or source. + image?: string; // Large image to display in the notification for attachments. + permanent?: boolean; // Whether or not the notification should be permanent or timeout. + dismissOnClick?: boolean; // Whether or not the notification should be dismissed when clicked. + attachments: number; + onClick?(): void; + onClose?(): void; +} + +export async function showNotification(notification: NotificationData) { + const root = getNotificationContainer(); + const thisNotificationID = notificationID++; + + return new Promise(resolve => { + const ToastNotification = ( + { + // Remove this notification from the queue. + NotificationQueue = NotificationQueue.filter(n => n.key !== thisNotificationID.toString()); + notification.onClose?.(); // Trigger the onClose callback if it exists. + console.log(`[DEBUG] [ToastNotifications] Removed #${thisNotificationID} from queue.`); + + // Re-render remaining notifications with new reversed indices. + root.render( + <> + {NotificationQueue.map((notification, index) => { + const reversedIndex = (NotificationQueue.length - 1) - index; + return React.cloneElement(notification, { index: reversedIndex }); + })} + + ); + + resolve(); + }} + /> + ); + + // Add this notification to the queue. + NotificationQueue.push(ToastNotification); + console.log(`[DEBUG] [ToastNotifications] Added #${thisNotificationID} to queue.`); + + // Limit the number of notifications to the configured maximum. + if (NotificationQueue.length > PluginSettings.store.maxNotifications) NotificationQueue.shift(); + + // Render the notifications. + root.render( + <> + {NotificationQueue.map((notification, index) => { + const reversedIndex = (NotificationQueue.length - 1) - index; + return React.cloneElement(notification, { index: reversedIndex }); + })} + + ); + }); +} diff --git a/src/equicordplugins/toastNotifications/components/styles.css b/src/equicordplugins/toastNotifications/components/styles.css new file mode 100644 index 00000000..89fd0ba0 --- /dev/null +++ b/src/equicordplugins/toastNotifications/components/styles.css @@ -0,0 +1,146 @@ +:root { + /* Body */ + --toastnotifications-background-color: var(--background-secondary-alt); + --toastnotifications-text-color: var(--text-normal); + --toastnotifications-border-radius: 6px; + --toastnotifications-width: 25vw; + --toastnotifications-padding: 1.25rem; + + /* Title */ + --toastnotifications-title-color: var(--header-primary); + --toastnotifications-title-font-size: 1rem; + --toastnotifications-title-font-weight: 600; + --toastnotifications-title-line-height: 1.25rem; + + /* Close Button */ + --toastnotifications-close-button-color: var(--interactive-normal); + --toastnotifications-close-button-hover-color: var(--interactive-hover); + --toastnotifications-close-button-opacity: 0.5; + --toastnotifications-close-button-hover-opacity: 1; + + /* Message Author Image */ + --toastnotifications-image-height: 4rem; + --toastnotifications-image-width: var(--toastnotifications-image-height); + --toastnotifications-image-border-radius: 6px; + + /* Progress Bar */ + --toastnotifications-progressbar-height: 0.25rem; + + /* Position Offset - Global inherited offset by all positions */ + --toastnotifications-position-offset: 1rem; +} + +.toastnotifications-notification-root { + all: unset; + display: flex; + flex-direction: column; + color: var(--toastnotifications-text-color); + background-color: var(--toastnotifications-background-color); + border-radius: var(--toastnotifications-border-radius); + overflow: hidden; + cursor: pointer; + position: absolute; + z-index: 2147483647; + right: 1rem; + width: var(--toastnotifications-width); + min-height: 10vh; + bottom: calc(1rem + var(--notification-index) * 12vh); +} + +.toastnotifications-notification { + display: flex; + flex-direction: row; + padding: var(--toastnotifications-padding); + gap: 1.25rem; +} + +.toastnotifications-notification-content { + width: 100%; +} + +.toastnotifications-notification-header { + display: flex; + justify-content: space-between; +} + +.toastnotifications-notification-title { + color: var(--toastnotifications-title-color); + font-size: var(--toastnotifications-title-font-size); + font-weight: var(--toastnotifications-title-font-weight); + line-height: var(--toastnotifications-title-line-height); +} + +.toastnotifications-notification-close-btn { + all: unset; + cursor: pointer; + color: var(--toastnotifications-close-button-color); + opacity: var(--toastnotifications-close-button-opacity); + transition: opacity 0.2s ease-in-out, color 0.2s ease-in-out; +} + +.toastnotifications-notification-close-btn:hover { + color: var(--toastnotifications-close-button-hover-color); + opacity: var(--toastnotifications-close-button-hover-opacity); +} + +.toastnotifications-notification-icon { + height: var(--toastnotifications-image-height); + width: var(--toastnotifications-image-width); + border-radius: var(--toastnotifications-image-border-radius); +} + +.toastnotifications-notification-progressbar { + height: var(--toastnotifications-progressbar-height); + border-radius: 5px; + margin-top: auto; +} + +.toastnotifications-notification-p { + margin: 0.5rem 0 0; + margin-bottom: 3px; + line-height: 140%; + word-break: break-all; +} + +.toastnotifications-notification-footer { + margin: 0; + margin-top: 4px; + line-height: 140%; + font-size: 10px; +} + +.toastnotifications-notification-img { + width: 75%; + border-radius: 3px; +} + +/* Notification Positioning CSS */ +.toastnotifications-position-bottom-left { + bottom: var(--toastnotifications-position-offset); + left: var(--toastnotifications-position-offset); +} + +.toastnotifications-position-top-left { + top: var(--toastnotifications-position-offset); + left: var(--toastnotifications-position-offset); +} + +.toastnotifications-position-top-right { + top: var(--toastnotifications-position-offset); + right: var(--toastnotifications-position-offset); +} + +.toastnotifications-position-bottom-right { + bottom: var(--toastnotifications-position-offset); + right: var(--toastnotifications-position-offset); +} + +/* Rich Body classes */ +.toastnotifications-mention-class { + color: var(--mention-foreground); + background: var(--mention-background); + /* stylelint-disable-next-line value-no-vendor-prefix */ + unicode-bidi: -moz-plaintext; + unicode-bidi: plaintext; + font-weight: 500; +} diff --git a/src/equicordplugins/toastNotifications/index.tsx b/src/equicordplugins/toastNotifications/index.tsx new file mode 100644 index 00000000..efc37f70 --- /dev/null +++ b/src/equicordplugins/toastNotifications/index.tsx @@ -0,0 +1,583 @@ +/* + * 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 . +*/ + +import { definePluginSettings } from "@api/Settings"; +import { makeRange } from "@components/PluginSettings/components"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { findByPropsLazy, findStore } from "@webpack"; +import { Button, ChannelStore, GuildStore, NavigationRouter, RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common"; +import { Channel, Message, User } from "discord-types/general"; +import { ReactNode } from "react"; + +import { NotificationData, showNotification } from "./components/Notifications"; +import { MessageTypes, RelationshipType, StreamingTreatment } from "./types"; + +let ignoredUsers: string[] = []; +let notifyFor: string[] = []; + +// Functional variables. +const MuteStore = findByPropsLazy("isSuppressEveryoneEnabled"); +const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); +const UserUtils = findByPropsLazy("getGlobalName"); + +// Adjustable variables. +const USER_MENTION_REGEX = /<@!?(\d{17,20})>|<#(\d{17,20})>|<@&(\d{17,20})>/g; // This regex captures user, channel, and role mentions. + +export const settings = definePluginSettings({ + position: { + type: OptionType.SELECT, + description: "The position of the toast notification", + options: [ + { + label: "Bottom Left", + value: "bottom-left", + default: true + }, + { + label: "Top Left", + value: "top-left" + }, + { + label: "Top Right", + value: "top-right" + }, + { + label: "Bottom Right", + value: "bottom-right" + }, + ] + }, + timeout: { + type: OptionType.SLIDER, + description: "Time in seconds notifications will be shown for", + default: 5, + markers: makeRange(1, 15, 1) + }, + opacity: { + type: OptionType.SLIDER, + description: "Opacity of the notification", + default: 100, + markers: makeRange(10, 100, 10) + }, + maxNotifications: { + type: OptionType.SLIDER, + description: "Maximum number of notifications displayed at once", + default: 3, + markers: makeRange(1, 5, 1) + }, + determineServerNotifications: { + type: OptionType.BOOLEAN, + description: "Automatically determine what server notifications to show based on your channel/guild settings", + default: true + }, + disableInStreamerMode: { + type: OptionType.BOOLEAN, + description: "Disable notifications while in streamer mode", + default: true + }, + renderImages: { + type: OptionType.BOOLEAN, + description: "Render images in notifications", + default: true + }, + directMessages: { + type: OptionType.BOOLEAN, + description: "Show notifications for direct messages", + default: true + }, + groupMessages: { + type: OptionType.BOOLEAN, + description: "Show notifications for group messages", + default: true + }, + friendServerNotifications: { + type: OptionType.BOOLEAN, + description: "Show notifications when friends send messages in servers they share with you", + default: true + }, + friendActivity: { + type: OptionType.BOOLEAN, + description: "Show notifications for adding someone or receiving a friend request", + default: true + }, + streamingTreatment: { + type: OptionType.SELECT, + description: "How to treat notifications while sharing your screen", + options: [ + { + label: "Normal - Show the notification as normal", + value: StreamingTreatment.NORMAL, + default: true + }, + { + label: "No Content - Hide the notification body", + value: StreamingTreatment.NO_CONTENT + }, + { + label: "Ignore - Don't show the notification at all", + value: StreamingTreatment.IGNORE + } + ] + }, + notifyFor: { + type: OptionType.STRING, + description: "Create a list of channel ids to receive notifications from (separate with commas)", + onChange: () => { notifyFor = stringToList(settings.store.notifyFor); }, + default: "" + }, + ignoreUsers: { + type: OptionType.STRING, + description: "Create a list of user ids to ignore all their notifications from (separate with commas)", + onChange: () => { ignoredUsers = stringToList(settings.store.ignoreUsers); }, + default: "" + }, + exampleButton: { + type: OptionType.COMPONENT, + description: "Show an example toast notification.", + component: () => + + } +}); + +function stringToList(str: string): string[] { + if (str !== "") { + const array: string[] = []; + const string = str.replace(/\s/g, ""); + const splitArray = string.split(","); + splitArray.forEach(id => { + array.push(id); + }); + + return array; + } + return []; +} + +function limitMessageLength(body: string, hasAttachments: boolean): string { + if (hasAttachments) { + if (body?.length > 30) { + return body.substring(0, 27) + "..."; + } + } + + if (body?.length > 165) { + return body.substring(0, 162) + "..."; + } + + return body; +} + +function getName(user: User): string { + return RelationshipStore.getNickname(user.id) ?? UserUtils.getName(user); +} + +const addMention = (id: string, type: string, guildId?: string): ReactNode => { + let name; + if (type === "user") + name = `@${UserStore.getUser(id)?.username || "unknown-user"}`; + else if (type === "channel") + name = `#${ChannelStore.getChannel(id)?.name || "unknown-channel"}`; + else if (type === "role" && guildId) + name = `@${GuildStore.getGuild(guildId).getRole(id)?.name || "unknown-role"}`; + + // Return the mention as a styled span. + return ( + + {name} + + ); +}; + +export default definePlugin({ + name: "ToastNotifications", + description: "Show a toast notification whenever you receive a direct message.", + authors: [EquicordDevs.Skully, EquicordDevs.Ethan, EquicordDevs.Buzzy], + settings, + flux: { + async MESSAGE_CREATE({ message }: { message: Message; }) { + + const channel: Channel = ChannelStore.getChannel(message.channel_id); + const currentUser = UserStore.getCurrentUser(); + + const isStreaming = findStore("ApplicationStreamingStore").getAnyStreamForUser(UserStore.getCurrentUser()?.id); + + const streamerMode = settings.store.disableInStreamerMode; + const currentUserStreamerMode = findStore("StreamerModeStore").enabled; + + if (streamerMode && currentUserStreamerMode) return; + if (isStreaming && settings.store.streamingTreatment === StreamingTreatment.IGNORE) return; + + if ( + ( + (message.author.id === currentUser.id) // If message is from the user. + || (channel.id === SelectedChannelStore.getChannelId()) // If the user is currently in the channel. + || (ignoredUsers.includes(message.author.id)) // If the user is ignored. + ) + ) return; + + if (channel.guild_id) { // If this is a guild message and not a private message. + handleGuildMessage(message); + return; + } + + if (!settings.store.directMessages && channel.isDM() || !settings.store.groupMessages && channel.isGroupDM() || MuteStore.isChannelMuted(null, channel.id)) return; + + // Prepare the notification. + const Notification: NotificationData = { + title: getName(message.author), + icon: `https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`, + body: message.content, + attachments: message.attachments?.length, + richBody: null, + permanent: false, + onClick() { SelectedChannelActionCreators.selectPrivateChannel(message.channel_id); } + }; + + const notificationText = message.content?.length > 0 ? message.content : false; + const richBodyElements: ReactNode[] = []; + + // If this channel is a group DM, include the channel name. + if (channel.isGroupDM()) { + let channelName = channel.name?.trim() ?? false; + if (!channelName) { // If the channel doesn't have a set name, use the first 3 recipients. + channelName = channel.rawRecipients.slice(0, 3).map(e => e.username).join(", "); + } + + // Finally, truncate the channel name if it's too long. + const truncatedChannelName = channelName?.length > 20 ? channelName.substring(0, 20) + "..." : channelName; + Notification.title = `${message.author.username} (${truncatedChannelName})`; + } + else if (channel.guild_id) // If this is a guild message and not a private message. + { + Notification.title = `${getName(message.author)} (#${channel.name})`; + } + + // Handle specific message types. + switch (message.type) { + case MessageTypes.CALL: { + Notification.body = "Started a call with you!"; + break; + } + case MessageTypes.CHANNEL_RECIPIENT_ADD: { + const actor = UserStore.getUser(message.author.id); + const user = message.mentions[0]; + const targetUser = UserStore.getUser((user as any).id); + + Notification.body = `${getName(targetUser)} was added to the group by ${getName(actor)}.`; + break; + } + case MessageTypes.CHANNEL_RECIPIENT_REMOVE: { + const actor = UserStore.getUser(message.author.id); + const user = message.mentions[0]; + const targetUser = UserStore.getUser((user as any).id); + + if (actor.id !== targetUser.id) { + Notification.body = `${getName(targetUser)} was removed from the group by ${getName(actor)}.`; + } else { + Notification.body = "Left the group."; + } + break; + } + case MessageTypes.CHANNEL_NAME_CHANGE: { + Notification.body = `Changed the channel name to '${message.content}'.`; + break; + } + case MessageTypes.CHANNEL_ICON_CHANGE: { + Notification.body = "Changed the channel icon."; + break; + } + case MessageTypes.CHANNEL_PINNED_MESSAGE: { + Notification.body = "Pinned a message."; + break; + } + } + + // Message contains an embed. + if (message.embeds?.length !== 0) { + Notification.body = notificationText || "Sent an embed."; + } + + // Message contains a sticker. + if (message?.stickerItems) { + Notification.body = notificationText || "Sent a sticker."; + } + + // Message contains an attachment. + if (message.attachments?.length !== 0) { + const images = message.attachments.filter(e => typeof e?.content_type === "string" && e?.content_type.startsWith("image")); + // Label the notification with the attachment type. + if (images?.length !== 0) { + Notification.body = notificationText || ""; // Dont show any body + Notification.image = images[0].url; + } else { + Notification.body += ` [Attachment: ${message.attachments[0].filename}]`; + } + } + + // TODO: Format emotes properly. + const matches = Notification.body.match(new RegExp("()", "g")); + if (matches) { + for (const match of matches) { + Notification.body = Notification.body.replace(new RegExp(`${match}`, "g"), `:${match.split(":")[1]}:`); + } + } + + // Replace any mention of users, roles and channels. + if (message.mentions?.length !== 0 || message.mentionRoles?.length > 0) { + let lastIndex = 0; + Notification.body.replace(USER_MENTION_REGEX, (match, userId, channelId, roleId, offset) => { + richBodyElements.push(Notification.body.slice(lastIndex, offset)); + + // Add the mention itself as a styled span. + if (userId) { + richBodyElements.push(addMention(userId, "user")); + } else if (channelId) { + richBodyElements.push(addMention(channelId, "channel")); + } else if (roleId) { + richBodyElements.push(addMention(roleId, "role", channel.guild_id)); + } + + lastIndex = offset + match?.length; + return match; // This value is not used but is necessary for the replace function + }); + } + + if (richBodyElements?.length > 0) { + const MyRichBodyComponent = () => <>{richBodyElements}; + Notification.richBody = ; + } + + Notification.body = limitMessageLength(Notification.body, Notification.attachments > 0); + + if (isStreaming && settings.store.streamingTreatment === StreamingTreatment.NO_CONTENT) { + Notification.body = "Message content has been redacted."; + } + + if (!settings.store.renderImages) { + Notification.icon = undefined; + } + + showNotification(Notification); + }, + + async RELATIONSHIP_ADD({ relationship }) { + if (ignoredUsers.includes(relationship.user.id)) return; + relationshipAdd(relationship.user, relationship.type); + } + }, + + start() { + ignoredUsers = stringToList(settings.store.ignoreUsers); + notifyFor = stringToList(settings.store.notifyFor); + } +}); + +function switchChannels(guildId: string | null, channelId: string) { + if (!ChannelStore.hasChannel(channelId)) return; + NavigationRouter.transitionTo(`/channels/${guildId ?? "@me"}/${channelId}/`); +} + +enum NotificationLevel { + ALL_MESSAGES = 0, + ONLY_MENTIONS = 1, + NO_MESSAGES = 2 +} + +function findNotificationLevel(channel: Channel): NotificationLevel { + const store = findStore("UserGuildSettingsStore"); + const userGuildSettings = store.getAllSettings().userGuildSettings[channel.guild_id]; + + if (!settings.store.determineServerNotifications || MuteStore.isGuildOrCategoryOrChannelMuted(channel.guild_id, channel.id)) { + return NotificationLevel.NO_MESSAGES; + } + + if (userGuildSettings) { + const channelOverrides = userGuildSettings.channel_overrides?.[channel.id]; + const guildDefault = userGuildSettings.message_notifications; + + // Check if channel overrides exist and are in the expected format + if (channelOverrides && typeof channelOverrides === "object" && "message_notifications" in channelOverrides) { + return channelOverrides.message_notifications; + } + + // Check if guild default is in the expected format + if (typeof guildDefault === "number") { + return guildDefault; + } + } + + // Return a default value if no valid overrides or guild default is found + return NotificationLevel.NO_MESSAGES; +} + +async function handleGuildMessage(message: Message) { + const c = ChannelStore.getChannel(message.channel_id); + const notificationLevel: number = findNotificationLevel(c); + let t = false; + // 0: All messages 1: Only mentions 2: No messages + // todo: check if the user who sent it is a friend + const all = notifyFor.includes(message.channel_id); + const friend = settings.store.friendServerNotifications && RelationshipStore.isFriend(message.author.id); + + + + if (!all && !friend) { + t = true; + const isMention: boolean = message.content.includes(`<@${UserStore.getCurrentUser().id}>`); + const meetsMentionCriteria = notificationLevel !== NotificationLevel.ALL_MESSAGES && !isMention; + + if (notificationLevel === NotificationLevel.NO_MESSAGES || meetsMentionCriteria) return; + } + + const channel: Channel = ChannelStore.getChannel(message.channel_id); + + const notificationText = message.content.length > 0 ? message.content : false; + const richBodyElements: React.ReactNode[] = []; + + // Prepare the notification. + const Notification: NotificationData = { + title: `${getName(message.author)} (#${channel.name})`, + icon: `https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`, + body: message.content, + attachments: message.attachments?.length, + richBody: null, + permanent: false, + onClick() { switchChannels(channel.guild_id, channel.id); } + }; + + if (message.embeds?.length !== 0) { + Notification.body = notificationText || "Sent an embed."; + } + + // Message contains a sticker. + if (message?.stickerItems) { + Notification.body = notificationText || "Sent a sticker."; + } + + // Message contains an attachment. + if (message.attachments?.length !== 0) { + const images = message.attachments.filter(e => typeof e?.content_type === "string" && e?.content_type.startsWith("image")); + // Label the notification with the attachment type. + if (images?.length !== 0) { + Notification.body = notificationText || ""; // Dont show any body + Notification.image = images[0].url; + } else { + Notification.body += ` [Attachment: ${message.attachments[0].filename}]`; + } + } + + // TODO: Format emotes properly. + const matches = Notification.body.match(new RegExp("()", "g")); + if (matches) { + for (const match of matches) { + Notification.body = Notification.body.replace(new RegExp(`${match}`, "g"), `:${match.split(":")[1]}:`); + } + } + + // Replace any mention of users, roles and channels. + if (message.mentions?.length !== 0 || message.mentionRoles?.length > 0) { + let lastIndex = 0; + Notification.body.replace(USER_MENTION_REGEX, (match, userId, channelId, roleId, offset) => { + richBodyElements.push(Notification.body.slice(lastIndex, offset)); + + // Add the mention itself as a styled span. + if (userId) { + richBodyElements.push(addMention(userId, "user")); + } else if (channelId) { + richBodyElements.push(addMention(channelId, "channel")); + } else if (roleId) { + richBodyElements.push(addMention(roleId, "role", channel.guild_id)); + } + + lastIndex = offset + match?.length; + return match; // This value is not used but is necessary for the replace function + }); + } + + if (richBodyElements?.length > 0) { + const MyRichBodyComponent = () => <>{richBodyElements}; + Notification.richBody = ; + } + + Notification.body = limitMessageLength(Notification.body, Notification.attachments > 0); + + const isStreaming = findStore("ApplicationStreamingStore").getAnyStreamForUser(UserStore.getCurrentUser()?.id); + + if (isStreaming && settings.store.streamingTreatment === StreamingTreatment.NO_CONTENT) { + Notification.body = "Message content has been redacted."; + } + + if (!settings.store.renderImages) { + Notification.icon = undefined; + } + + console.log("noti that went through: " + t); + await showNotification(Notification); + +} + +async function relationshipAdd(user: User, type: Number) { + user = UserStore.getUser(user.id); + if (!settings.store.friendActivity) return; + + const Notification: NotificationData = { + title: "", + icon: user.getAvatarURL(), + body: "", + attachments: 0, + }; + + if (!settings.store.renderImages) { + Notification.icon = undefined; + } + + if (type === RelationshipType.FRIEND) { + Notification.title = `${user.username} is now your friend`; + Notification.body = "You can now message them directly."; + Notification.onClick = () => switchChannels(null, user.id); + + + await showNotification(Notification); + + } else if (type === RelationshipType.INCOMING_REQUEST) { + + Notification.title = `${user.username} sent you a friend request`; + Notification.body = "You can accept or decline it in the Friends tab."; + Notification.onClick = () => switchChannels(null, ""); + + await showNotification(Notification); + } +} + +function showExampleNotification(): Promise { + const Notification: NotificationData = { + title: "Example Notification", + icon: `https://cdn.discordapp.com/avatars/${UserStore.getCurrentUser().id}/${UserStore.getCurrentUser().avatar}.png?size=128`, + body: "This is an example toast notification!", + attachments: 0, + permanent: false + }; + + if (!settings.store.renderImages) { + Notification.icon = undefined; + } + return showNotification(Notification); +} diff --git a/src/equicordplugins/toastNotifications/types.ts b/src/equicordplugins/toastNotifications/types.ts new file mode 100644 index 00000000..d3c444f1 --- /dev/null +++ b/src/equicordplugins/toastNotifications/types.ts @@ -0,0 +1,39 @@ +/* + * 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 . +*/ + +export const enum MessageTypes { + CHANNEL_RECIPIENT_ADD = 1, + CHANNEL_RECIPIENT_REMOVE = 2, + CALL = 3, + CHANNEL_NAME_CHANGE = 4, + CHANNEL_ICON_CHANGE = 5, + CHANNEL_PINNED_MESSAGE = 6, +} + +export const enum RelationshipType { + FRIEND = 1, + BLOCKED = 2, + INCOMING_REQUEST = 3, + OUTGOING_REQUEST = 4, +} + +export const enum StreamingTreatment { + NORMAL = 0, + NO_CONTENT = 1, + IGNORE = 2 +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index f8aabc88..6baba55e 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1050,6 +1050,14 @@ export const EquicordDevs = Object.freeze({ name: "byeoon", id: 1167275288036655133n }, + Skully: { + name: "Skully", + id: 150298098516754432n + }, + Buzzy: { + name: "Buzzy", + id: 1273353654644117585n + }, } satisfies Record); // iife so #__PURE__ works correctly From 5db0cea8c374722a8066404dbc27af704bfb7024 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 29 Apr 2025 01:45:12 -0400 Subject: [PATCH 068/198] Fix CorsProxy --- src/equicordplugins/betterAudioPlayer/index.tsx | 2 +- src/equicordplugins/moreStickers/utils.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/betterAudioPlayer/index.tsx b/src/equicordplugins/betterAudioPlayer/index.tsx index 0f942e90..6264db1f 100644 --- a/src/equicordplugins/betterAudioPlayer/index.tsx +++ b/src/equicordplugins/betterAudioPlayer/index.tsx @@ -66,7 +66,7 @@ async function addListeners(audioElement: HTMLAudioElement, url: string, parentB const madeURL = new URL(url); madeURL.searchParams.set("t", Date.now().toString()); - const corsProxyUrl = "https://corsproxy.io?" + encodeURIComponent(madeURL.href); + const corsProxyUrl = "https://corsproxy.io/?url=" + encodeURIComponent(madeURL.href); const response = await fetch(corsProxyUrl); const blob = await response.blob(); const blobUrl = URL.createObjectURL(blob); diff --git a/src/equicordplugins/moreStickers/utils.tsx b/src/equicordplugins/moreStickers/utils.tsx index f73a5e4c..0af5de35 100644 --- a/src/equicordplugins/moreStickers/utils.tsx +++ b/src/equicordplugins/moreStickers/utils.tsx @@ -14,7 +14,7 @@ import { FFmpegState } from "./types"; export const cl = classNameFactory("vc-more-stickers-"); export const clPicker = (className: string, ...args: any[]) => cl("picker-" + className, ...args); -const CORS_PROXY = "https://corsproxy.io?"; +const CORS_PROXY = "https://corsproxy.io/?url="; function corsUrl(url: string | URL) { return CORS_PROXY + encodeURIComponent(url.toString()); From 493afb254cef8021564d8a357c20b062e1b47ddc Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 29 Apr 2025 05:05:03 -0400 Subject: [PATCH 069/198] Add ForwardAnywhere --- README.md | 5 +- .../betterBlockedUsers/index.tsx | 190 +++--------------- .../betterBlockedUsers/styles.css | 3 + src/equicordplugins/forwardAnywhere/index.ts | 39 ++++ 4 files changed, 72 insertions(+), 165 deletions(-) create mode 100644 src/equicordplugins/betterBlockedUsers/styles.css create mode 100644 src/equicordplugins/forwardAnywhere/index.ts diff --git a/README.md b/README.md index 3b3b6071..e8af6a0e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-168 additional plugins +169 additional plugins ### All Platforms @@ -25,7 +25,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - BetterActivities by D3SOX, Arjix, AutumnVN - BetterAudioPlayer by Creations - BetterBanReasons by Inbestigator -- BetterBlockedUsers by TheArmagan & Elvyra +- BetterBlockedUsers by TheArmagan - BetterInvites by iamme - BetterPlusReacts by Joona - BetterQuickReact by Ven & Sqaaakoi @@ -63,6 +63,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - FixFileExtensions by thororen - FollowVoiceUser by TheArmagan - FontLoader by vmohammad +- ForwardAnywhere by thororen - Freaky by nyx - FrequentQuickSwitcher by Samwich - FriendCodes by HypedDomi diff --git a/src/equicordplugins/betterBlockedUsers/index.tsx b/src/equicordplugins/betterBlockedUsers/index.tsx index f1a5efb5..87876bbf 100644 --- a/src/equicordplugins/betterBlockedUsers/index.tsx +++ b/src/equicordplugins/betterBlockedUsers/index.tsx @@ -4,57 +4,23 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { definePluginSettings } from "@api/Settings"; -import { Devs, EquicordDevs } from "@utils/constants"; -import { openUserProfile } from "@utils/discord"; -import { openModal } from "@utils/modal"; -import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import { Button, FluxDispatcher, React, RelationshipStore, showToast, Text, TextInput, UserStore } from "@webpack/common"; -import { ButtonProps } from "@webpack/types"; -import { User } from "discord-types/general"; +import "./styles.css"; + +import { EquicordDevs } from "@utils/constants"; +import { getIntlMessage, openUserProfile } from "@utils/discord"; +import definePlugin from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { Button, React, RelationshipStore, TextInput, UserStore } from "@webpack/common"; + +const ChannelActions = findByPropsLazy("openPrivateChannel"); let lastSearch = ""; let updateFunc = (v: any) => { }; -const ChannelActions = findByPropsLazy("openPrivateChannel"); -const ButtonComponent = findComponentByCodeLazy('submittingStartedLabel","submittingFinishedLabel"]);'); -const ConfirmationModal = findByCodeLazy('"ConfirmModal")', "useLayoutEffect"); - -const settings = definePluginSettings({ - addDmsButton: { - default: true, - type: OptionType.BOOLEAN, - description: "Adds a 'View DMs' button to the users in the blocked/ignored list.", - }, - showUnblockConfirmation: { - default: true, - type: OptionType.BOOLEAN, - description: "Show a warning before unblocking a user from the blocklist.", - }, - showUnblockConfirmationEverywhere: { - default: false, - type: OptionType.BOOLEAN, - description: "Show a warning before unblocking a user anywhere on discord.", - restartNeeded: true, - }, - unblockButtonDanger: { - default: false, - type: OptionType.BOOLEAN, - description: "Color the unblock button in the blocklist red instead of gray.", - }, - allowShiftUnblock: { - default: true, - type: OptionType.BOOLEAN, - description: "Unblock a user without confirmation prompting when holding shift.", - } -}); - export default definePlugin({ name: "BetterBlockedUsers", - description: "Allows you to search in blocked users list and makes names clickable in settings.", - authors: [EquicordDevs.TheArmagan, Devs.Elvyra], - settings, + description: "Allows you to search in blocked users list and makes names selectable in settings.", + authors: [EquicordDevs.TheArmagan], patches: [ { find: '"],{numberOfBlockedUsers:', @@ -64,12 +30,8 @@ export default definePlugin({ replace: ",$1.listType==='blocked'?$self.renderSearchInput():null" }, { - match: /(?<=className:\i.userInfo,)(?=children:.{0,20}user:(\i))/, - replace: "style:{cursor:'pointer'},onClick:()=>$self.openUserProfile($1)," - }, - { - match: /(?<=children:null!=(\i).globalName\?.+?}\),).*?(\{color:.{0,65}?string\((\i).+?"8wXU9P"]\)})\)/, - replace: "$self.generateButtons({user:$1, originalProps:$2, isBlocked:$3})", + match: /(?<=userId:(\i).*?\}\)\]\}\),)(\(.*?\)\}\))/, + replace: "$self.renderUser($1,$2),", }, { match: /(?<=\}=(\i).{0,10}(\i).useState\(.{0,1}\);)/, @@ -80,45 +42,6 @@ export default definePlugin({ replace: "$1(searchResults.length?searchResults:$2)" }, ] - }, - { - find: "#{intl::OUTGOING_FRIEND_REQUEST}", - group: true, - replacement: [ - { - match: /(?<=return \i)\|\|(\i)===.*?.FRIEND/, - replace: (_, type) => `?null:${type} === 1|| ${type} === 2`, - }, - { - match: /(?<=\i.bot.{0,50}children:.*?onClose:)(\i)/, - replace: "() => {$1();$self.closeSettingsWindow()}", - } - ], - }, - - { - find: ".BLOCKED:return", - replacement: { - match: /(?<=\i.BLOCKED:return.{0,65}onClick:)\(\)=>\{(\i.\i.unblockUser\((\i).+?}\))/, - replace: "(event) => {$self.openConfirmationModal(event,()=>{$1}, $2)", - }, - predicate: () => settings.store.showUnblockConfirmationEverywhere, - }, - { - find: "#{intl::UNBLOCK}),", - replacement: { - match: /(?<=#{intl::UNBLOCK}.+?Click=)\(\)=>(\{.+?(\i.getRecipientId\(\))\)})/, - replace: "event => $self.openConfirmationModal(event, ()=>$1, $2)", - }, - predicate: () => settings.store.showUnblockConfirmationEverywhere, - }, - { - find: "#{intl::BLOCK}),action", - replacement: { - match: /(?<=id:"block".{0,100}action:\i\?)\(\)=>(\{.{0,25}unblockUser\((\i).{0,60}:void 0\)})/, - replace: "event => {$self.openConfirmationModal(event, ()=>$1,$2)}", - }, - predicate: () => settings.store.showUnblockConfirmationEverywhere, } ], renderSearchInput() { @@ -141,6 +64,19 @@ export default definePlugin({ }} value={value} >; }, + renderUser(userId: string, rest: any) { + return ( +
+ + {rest} +
+ ); + }, + getSearchResults() { + return !!lastSearch; + }, setUpdateFunc(e, setResults) { if (e.listType !== "blocked") return; updateFunc = setResults; @@ -153,77 +89,5 @@ export default definePlugin({ if (!user) return id === search; return id === search || user?.username?.toLowerCase()?.includes(search) || user?.globalName?.toLowerCase()?.includes(search); }) as string[]; - }, - closeSettingsWindow() { - FluxDispatcher.dispatch({ type: "LAYER_POP" }); - }, - openUserProfile(user: User) { - openUserProfile(user.id); - }, - generateButtons(props: { user: User, originalProps: ButtonProps, isBlocked: boolean; }) { - const { user, originalProps, isBlocked } = props; - - if (settings.store.unblockButtonDanger) originalProps.color = Button.Colors.RED; - - if (settings.store.showUnblockConfirmation || settings.store.showUnblockConfirmationEverywhere) { - const originalOnClick = originalProps.onClick!; - originalProps.onClick = e => { - if (!isBlocked) return originalOnClick(e); - this.openConfirmationModal(e as unknown as MouseEvent, () => originalOnClick(e), user, true); - }; - } - - const unblockButton = ; - - if (!settings.store.addDmsButton) return unblockButton; - - const dmButton = this.openDMChannel(user)}>Show DMs; - - return
- {dmButton} - {unblockButton} -
; - }, - - openDMChannel(user: User) { - try { - ChannelActions.openPrivateChannel(user.id); - } - catch (e) { - showToast("Failed to open DMs for user '" + user.username + "'! Check the console for more info"); - return console.error(e); - } - // only close the settings window if we actually opened a DM channel behind it. - this.closeSettingsWindow(); - }, - - openConfirmationModal(event: MouseEvent, callback: () => any, user: User | string, isSettingsOrigin: boolean = false) { - if (event.shiftKey && settings.store.allowShiftUnblock) return callback(); - - if (typeof user === "string") { - user = UserStore.getUser(user); - } - - return openModal(m => { - callback(); - }}> -
-
- {`Are you sure you want to unblock ${user?.username ?? "this user"}?`} - {`This will allow ${user?.username ?? "them"} to see your profile and message you again.`} -
- {"You can always block them again later."} - {isSettingsOrigin ?
- {"If you just want to read the chat logs instead, you can just click on their profile."} - {"Alternatively, you can enable a button to jump to DMs in the blocklist through the plugin settings."} -
: {"If you just want to read the chat logs, you can do this without unblocking them."}} -
-
); - }, + } }); diff --git a/src/equicordplugins/betterBlockedUsers/styles.css b/src/equicordplugins/betterBlockedUsers/styles.css new file mode 100644 index 00000000..810c6772 --- /dev/null +++ b/src/equicordplugins/betterBlockedUsers/styles.css @@ -0,0 +1,3 @@ +[class*="usersList_"] [class*="text_"] { + user-select: text !important; +} diff --git a/src/equicordplugins/forwardAnywhere/index.ts b/src/equicordplugins/forwardAnywhere/index.ts new file mode 100644 index 00000000..806e8aa7 --- /dev/null +++ b/src/equicordplugins/forwardAnywhere/index.ts @@ -0,0 +1,39 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { EquicordDevs } from "@utils/constants"; +import { sendMessage } from "@utils/discord"; +import definePlugin from "@utils/types"; +import { Message } from "discord-types/general"; + +export default definePlugin({ + name: "ForwardAnywhere", + description: "If a forward fails send it as a normal message also allows nsfw forwards", + authors: [EquicordDevs.thororen], + patches: [ + { + find: "#{intl::MESSAGE_FORWARDING_NSFW_NOT_ALLOWED}", + replacement: { + match: /if\((\i)\.isNSFW\(\)&&.{0,25}\)\)\)/, + replace: "if(false)", + } + }, + { + find: "#{intl::MESSAGE_ACTION_FORWARD_TO}", + replacement: { + match: /(?<=let (\i)=.{0,25}rejected.{0,25}\);)(?=.{0,25}message:(\i))/, + replace: "if ($1) return $self.sendForward($1,$2);", + } + }, + ], + sendForward(channels: any, message: Message) { + for (const c of channels) { + sendMessage(c.id, { + content: `${message.content}\n\n> Forwarded from <#${message.channel_id}>` + }); + } + } +}); From d1f9476faa28bc2079bb839400f2203972929444 Mon Sep 17 00:00:00 2001 From: "Indiana Jone (Indi)" <150982280+KrstlSkll69@users.noreply.github.com> Date: Tue, 29 Apr 2025 16:56:43 -0400 Subject: [PATCH 070/198] Update Signature, Css fix (#248) * Update Signature * update Plugin Stats cards --- src/components/PluginSettings/styles.css | 22 ++++++++++++++++++++-- src/equicordplugins/signature/index.tsx | 10 +++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/components/PluginSettings/styles.css b/src/components/PluginSettings/styles.css index 1eabaa1b..ce131729 100644 --- a/src/components/PluginSettings/styles.css +++ b/src/components/PluginSettings/styles.css @@ -252,6 +252,24 @@ } .visual-refresh .button-danger-background:hover { - background-color: var(--status-danger-background) !important; - color: var(--status-danger-text) !important; + background-color: var(--status-danger-background) !important; + color: var(--status-danger-text) !important; +} + +.visual-refresh .vc-plugins-info-card { + background-color: var(--card-primary-bg) !important; + border: 1px solid var(--border-subtle) !important; + + &:hover { + background-color: var(--card-primary-bg) !important; + } + } + +.visual-refresh .vc-plugin-stats { + background-color: var(--card-primary-bg) !important; + border: 1px solid var(--border-subtle) !important; + + &:hover { + background-color: var(--card-primary-bg) !important; + } } diff --git a/src/equicordplugins/signature/index.tsx b/src/equicordplugins/signature/index.tsx index fbe12901..f027ffb4 100644 --- a/src/equicordplugins/signature/index.tsx +++ b/src/equicordplugins/signature/index.tsx @@ -22,6 +22,14 @@ const settings = definePluginSettings( description: "The signature that will be added to the end of your messages", default: "a chronic discord user" }, + textHeader: { + description: "What header to preface text with", + type: OptionType.SELECT, + options: [ + { label: ">", value: ">", default: true }, + { label: "-#", value: "-#" } + ] + }, showIcon: { type: OptionType.BOOLEAN, default: true, @@ -141,7 +149,7 @@ export default definePlugin({ // text processing injection processor function textProcessing(input: string) { - return `${input}\n> ${settings.store.name}`; + return `${input}\n${settings.store.textHeader} ${settings.store.name}`; } From 2db000c4ce7ebe60941d3f95e63a36c1847b0a29 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 29 Apr 2025 18:25:44 -0400 Subject: [PATCH 071/198] Some Fixes --- src/equicordplugins/forwardAnywhere/index.ts | 17 +++++++++++++++-- src/equicordplugins/githubRepos/index.tsx | 2 +- .../components/Notifications.tsx | 5 ----- src/plugins/friendsSince/index.tsx | 2 +- src/utils/quickCss.ts | 12 ++---------- src/webpack/common/react.ts | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/equicordplugins/forwardAnywhere/index.ts b/src/equicordplugins/forwardAnywhere/index.ts index 806e8aa7..d84f7e1e 100644 --- a/src/equicordplugins/forwardAnywhere/index.ts +++ b/src/equicordplugins/forwardAnywhere/index.ts @@ -4,15 +4,28 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { definePluginSettings } from "@api/Settings"; import { EquicordDevs } from "@utils/constants"; import { sendMessage } from "@utils/discord"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import { Message } from "discord-types/general"; +const settings = definePluginSettings({ + textHeader: { + description: "What header to preface text with", + type: OptionType.SELECT, + options: [ + { label: ">", value: ">", default: true }, + { label: "-#", value: "-#" } + ] + } +}); + export default definePlugin({ name: "ForwardAnywhere", description: "If a forward fails send it as a normal message also allows nsfw forwards", authors: [EquicordDevs.thororen], + settings, patches: [ { find: "#{intl::MESSAGE_FORWARDING_NSFW_NOT_ALLOWED}", @@ -32,7 +45,7 @@ export default definePlugin({ sendForward(channels: any, message: Message) { for (const c of channels) { sendMessage(c.id, { - content: `${message.content}\n\n> Forwarded from <#${message.channel_id}>` + content: `${message.content}\n${settings.store.textHeader} Forwarded from <#${message.channel_id}>` }); } } diff --git a/src/equicordplugins/githubRepos/index.tsx b/src/equicordplugins/githubRepos/index.tsx index 7faa03c9..65eccb64 100644 --- a/src/equicordplugins/githubRepos/index.tsx +++ b/src/equicordplugins/githubRepos/index.tsx @@ -76,7 +76,7 @@ export default definePlugin({ } }, { - find: "action:\"PRESS_APP_CONNECTION\"", + find: "#{intl::CONNECTIONS}),scrollIntoView", replacement: { match: /(?<=user:(\i).{0,15}displayProfile:(\i).*?CONNECTIONS.{0,100}\}\)\}\))/, replace: ",$self.ProfilePopoutComponent({ user: $1, displayProfile: $2 })" diff --git a/src/equicordplugins/toastNotifications/components/Notifications.tsx b/src/equicordplugins/toastNotifications/components/Notifications.tsx index fa10e2ae..204ac6e3 100644 --- a/src/equicordplugins/toastNotifications/components/Notifications.tsx +++ b/src/equicordplugins/toastNotifications/components/Notifications.tsx @@ -27,11 +27,6 @@ let NotificationQueue: JSX.Element[] = []; let notificationID = 0; let RootContainer: Root; -/** - * getNotificationContainer() - * Gets the root container for the notifications, creating it if it doesn't exist. - * @returns {Root} The root DOM container. - */ function getNotificationContainer() { if (!RootContainer) { const container = document.createElement("div"); diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index 07723bcc..ad4e8eab 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -32,7 +32,7 @@ export default definePlugin({ }, // User Profile Modal { - find: "action:\"PRESS_APP_CONNECTION\"", + find: "#{intl::CONNECTIONS}),scrollIntoView", replacement: { match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id),.{0,100}}\)}\),/, replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:false})," diff --git a/src/utils/quickCss.ts b/src/utils/quickCss.ts index 71092e51..17fa15e5 100644 --- a/src/utils/quickCss.ts +++ b/src/utils/quickCss.ts @@ -17,8 +17,7 @@ */ import { Settings, SettingsStore } from "@api/Settings"; -import { ThemeStore } from "@webpack/common"; - +import { findByCodeLazy } from "@webpack"; let style: HTMLStyleElement; let themesStyle: HTMLStyleElement; @@ -62,6 +61,7 @@ async function initThemes() { const enabledlinks: string[] = [...enabledThemeLinks]; // "darker" and "midnight" both count as dark + const ThemeStore = findByCodeLazy("ThemeStore"); const activeTheme = ThemeStore.theme === "light" ? "light" : "dark"; const links = enabledlinks @@ -99,14 +99,6 @@ document.addEventListener("DOMContentLoaded", () => { SettingsStore.addChangeListener("enabledThemeLinks", initThemes); SettingsStore.addChangeListener("enabledThemes", initThemes); - let currentTheme = ThemeStore.theme; - ThemeStore.addChangeListener(() => { - if (currentTheme === ThemeStore.theme) return; - - currentTheme = ThemeStore.theme; - initThemes(); - }); - if (!IS_WEB) VencordNative.quickCss.addThemeChangeListener(initThemes); }); diff --git a/src/webpack/common/react.ts b/src/webpack/common/react.ts index 1bb874e8..90f071d2 100644 --- a/src/webpack/common/react.ts +++ b/src/webpack/common/react.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { findByPropsLazy, waitFor } from "@webpack"; +import { findByPropsLazy, waitFor } from "../webpack"; export let React: typeof import("react"); export let useState: typeof React.useState; From 2de0503402f5ede6243ecd82634f4d1afbe7b586 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 29 Apr 2025 19:27:58 -0400 Subject: [PATCH 072/198] feat(ForwardAnywhere): Rename Setting --- src/equicordplugins/forwardAnywhere/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/equicordplugins/forwardAnywhere/index.ts b/src/equicordplugins/forwardAnywhere/index.ts index d84f7e1e..c2ca185f 100644 --- a/src/equicordplugins/forwardAnywhere/index.ts +++ b/src/equicordplugins/forwardAnywhere/index.ts @@ -10,9 +10,10 @@ import { sendMessage } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { Message } from "discord-types/general"; +// Taken From Signature :) const settings = definePluginSettings({ - textHeader: { - description: "What header to preface text with", + forwardPreface: { + description: "What should forwarded from be prefaced with", type: OptionType.SELECT, options: [ { label: ">", value: ">", default: true }, @@ -45,7 +46,7 @@ export default definePlugin({ sendForward(channels: any, message: Message) { for (const c of channels) { sendMessage(c.id, { - content: `${message.content}\n${settings.store.textHeader} Forwarded from <#${message.channel_id}>` + content: `${message.content}\n${settings.store.forwardPreface} Forwarded from <#${message.channel_id}>` }); } } From dad69e0d0fea7163385e72868ca16670b50659fd Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 30 Apr 2025 03:11:56 +0200 Subject: [PATCH 073/198] Fix Vencord Notifications & ImageZoom causing crashes --- src/plugins/imageZoom/index.tsx | 15 ++++++++++----- src/webpack/common/react.ts | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index 25a0ab7d..d7e8d487 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -21,6 +21,7 @@ import { definePluginSettings } from "@api/Settings"; import { makeRange } from "@components/PluginSettings/components"; import { debounce } from "@shared/debounce"; import { Devs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; import { Menu, ReactDOM } from "@webpack/common"; import { JSX } from "react"; @@ -237,12 +238,16 @@ export default definePlugin({ }, renderMagnifier(instance) { - if (instance.props.id === ELEMENT_ID) { - if (!this.currentMagnifierElement) { - this.currentMagnifierElement = ; - this.root = ReactDOM.createRoot(this.element!); - this.root.render(this.currentMagnifierElement); + try { + if (instance.props.id === ELEMENT_ID) { + if (!this.currentMagnifierElement) { + this.currentMagnifierElement = ; + this.root = ReactDOM.createRoot(this.element!); + this.root.render(this.currentMagnifierElement); + } } + } catch (error) { + new Logger("ImageZoom").error("Failed to render magnifier:", error); } }, diff --git a/src/webpack/common/react.ts b/src/webpack/common/react.ts index 99f3f9dd..aa279d65 100644 --- a/src/webpack/common/react.ts +++ b/src/webpack/common/react.ts @@ -28,7 +28,7 @@ export let useRef: typeof React.useRef; export let useReducer: typeof React.useReducer; export let useCallback: typeof React.useCallback; -export const ReactDOM: typeof import("react-dom") & typeof import("react-dom/client") = findByPropsLazy("createPortal", "render"); +export const ReactDOM: typeof import("react-dom") & typeof import("react-dom/client") = findByPropsLazy("createPortal"); waitFor("useState", m => { React = m; From 9fa91c193d78fa8a0c32131c0ffa8aec70931c5e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 30 Apr 2025 03:58:46 +0200 Subject: [PATCH 074/198] Fix ServerInfo, ShowMeYourName & WebContextMenus --- src/plugins/serverInfo/GuildInfoModal.tsx | 2 +- src/plugins/serverInfo/styles.css | 1 - src/plugins/showMeYourName/index.tsx | 5 +++-- src/plugins/webContextMenus.web/index.ts | 9 ++++++++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/plugins/serverInfo/GuildInfoModal.tsx b/src/plugins/serverInfo/GuildInfoModal.tsx index 9f2d3008..0f08af59 100644 --- a/src/plugins/serverInfo/GuildInfoModal.tsx +++ b/src/plugins/serverInfo/GuildInfoModal.tsx @@ -16,7 +16,7 @@ import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, GuildStore, import { Guild, User } from "discord-types/general"; const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); -const FriendRow = findComponentByCodeLazy(".listName,discriminatorClass"); +const FriendRow = findComponentByCodeLazy("discriminatorClass:", ".isMobileOnline", "getAvatarURL"); const cl = classNameFactory("vc-gp-"); diff --git a/src/plugins/serverInfo/styles.css b/src/plugins/serverInfo/styles.css index 274b7d13..42a7899c 100644 --- a/src/plugins/serverInfo/styles.css +++ b/src/plugins/serverInfo/styles.css @@ -50,7 +50,6 @@ border-bottom: 2px solid transparent; color: var(--interactive-normal); cursor: pointer; - height: 39px; line-height: 14px; } diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index 1f04f1f3..ac727f69 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -48,9 +48,10 @@ export default definePlugin({ authors: [Devs.Rini, Devs.TheKodeToad], patches: [ { - find: '?"@":""', + find: '"BaseUsername"', replacement: { - match: /(?<=onContextMenu:\i,children:)\i\+\i/, + /* TODO: remove \i+\i once change makes it to stable */ + match: /(?<=onContextMenu:\i,children:)(?:\i\+\i|\i)/, replace: "$self.renderUsername(arguments[0])" } }, diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index a0f84dd6..45e6fa00 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -120,6 +120,13 @@ export default definePlugin({ ] }, + { + find: "Copy image not supported", + replacement: { + match: /(?<=(?:canSaveImage|canCopyImage)\(\i?\)\{.{0,50})!\i\.isPlatformEmbedded/g, + replace: "false" + } + }, // Add back Copy & Save Image { find: 'id:"copy-image"', @@ -130,7 +137,7 @@ export default definePlugin({ replace: "false" }, { - match: /return\s*?\[\i\.\i\.canCopyImage\(\)/, + match: /return\s*?\[.{0,50}?(?=\?.{0,100}?id:"copy-image")/, replace: "return [true" }, { From 096f8483852a0edf1e4e472f250f396237c662b5 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 30 Apr 2025 04:13:31 +0200 Subject: [PATCH 075/198] MutualGroupDMs: fix weird spacing --- src/plugins/mutualGroupDMs/style.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/mutualGroupDMs/style.css b/src/plugins/mutualGroupDMs/style.css index 14ea83c9..3d06568f 100644 --- a/src/plugins/mutualGroupDMs/style.css +++ b/src/plugins/mutualGroupDMs/style.css @@ -1,4 +1,3 @@ .vc-mutual-gdms-tab-bar { gap: 0; - justify-content: space-between; } From 3359e66e80b191cf0d1c77832de5c6dbc373174c Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 30 Apr 2025 08:17:37 -0400 Subject: [PATCH 076/198] Fix Import --- src/webpack/common/react.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webpack/common/react.ts b/src/webpack/common/react.ts index f1330bdc..bd5b1b53 100644 --- a/src/webpack/common/react.ts +++ b/src/webpack/common/react.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { findByPropsLazy, waitFor } from "../webpack"; +import { findByPropsLazy, waitFor } from "@webpack"; export let React: typeof import("react"); export let useState: typeof React.useState; From b954bf3c9d9329e55c9dc2bd93ac81944a8bdd21 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 30 Apr 2025 03:30:37 +0200 Subject: [PATCH 077/198] MutualGroupDMs: fix DM sidebar (again) --- src/plugins/mutualGroupDMs/index.tsx | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 3c8e30fe..42f4f2e3 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -32,8 +32,8 @@ const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); const UserUtils = findByPropsLazy("getGlobalName"); const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds"); +const MutualsListClasses = findByPropsLazy("row", "icon", "name", "nick"); const ExpandableList = findComponentByCodeLazy('"PRESS_SECTION"', ".header"); -const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon"); function getGroupDMName(channel: Channel) { return channel.name || @@ -59,21 +59,22 @@ function renderClickableGDMs(mutualDms: Channel[], onClose: () => void) { return mutualDms.map(c => ( { onClose(); SelectedChannelActionCreators.selectPrivateChannel(c.id); }} > - - -
-
{getGroupDMName(c)}
-
{c.recipients.length + 1} Members
+
+ + +
+
{getGroupDMName(c)}
+
{c.recipients.length + 1} Members
+
)); From 06c3ea5b01431c1f60d1a5e79414dbf66e11ced1 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:26:04 -0400 Subject: [PATCH 078/198] Fix MoreStickers --- src/equicordplugins/moreStickers/components/picker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/moreStickers/components/picker.tsx b/src/equicordplugins/moreStickers/components/picker.tsx index 27068391..057d42cc 100644 --- a/src/equicordplugins/moreStickers/components/picker.tsx +++ b/src/equicordplugins/moreStickers/components/picker.tsx @@ -449,7 +449,7 @@ export const PickerHeader = ({ onQueryChange }: PickerHeaderProps) => {
Date: Wed, 30 Apr 2025 22:56:18 -0400 Subject: [PATCH 079/198] Fixes For Patches Co-Authored-By: sadan4 <117494111+sadan4@users.noreply.github.com> --- src/plugins/betterFolders/index.tsx | 42 +++++++++++++++--------- src/plugins/plainFolderIcon/index.ts | 21 ++++++------ src/plugins/plainFolderIcon/styles.css | 12 +++++++ src/plugins/reviewDB/index.tsx | 3 +- src/plugins/showHiddenChannels/index.tsx | 2 +- 5 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 src/plugins/plainFolderIcon/styles.css diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 1bb52944..ef12ff8f 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -119,7 +119,7 @@ export default definePlugin({ predicate: () => settings.store.sidebar, replacement: [ // Create the isBetterFolders variable in the GuildsBar component - // Needed because we access this from a closure so we can't use arguments[0] + // Needed because we access this from a non-arrow closure so we can't use arguments[0] { match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i,)/, replace: "$&,isBetterFolders" @@ -156,6 +156,18 @@ export default definePlugin({ match: /(?=\(0,\i\.jsxs?\)[^0]+fullWidth:)/, replace: "!!arguments[0]?.isBetterFolders?null:" }, + // On the other sidebar version, dms are rendered separately. + // We need to filter them out + { + match: /fullWidth:!0.+?lurkingGuildIds.+?\]/, + replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0]?.isBetterFolders))" + }, + // if you click the (NEW) button on the better folders sidebar + // it will end up in some infinite loop + { + match: /unreadMentionsFixedFooter\].+?\]/, + replace: "$&.filter($self.makeNewButtonFilter(!!arguments[0]?.isBetterFolders))" + }, // Export the isBetterFolders variable to the folders component { match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/, @@ -184,7 +196,7 @@ export default definePlugin({ ] }, { - find: ".expandedFolderBackground,", + find: ".FOLDER_ITEM_ANIMATION_DURATION),", predicate: () => settings.store.sidebar, replacement: [ // We use arguments[0] to access the isBetterFolders variable in this nested folder component (the parent exports all the props so we don't have to patch it) @@ -204,27 +216,20 @@ export default definePlugin({ // If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded { predicate: () => !settings.store.keepIcons, - match: /expandedFolderBackground,.+?,(?=\i\(\(\i,\i,\i\)=>{let{key.{0,45}ul)(?<=selected:\i,expanded:(\i),.+?)/, + match: /folderGroupBackground.*?,(?=\i\(\(\i,\i,\i\)=>{let{key.+?"ul")(?<=selected:\i,expanded:(\i),.+?)/, replace: (m, isExpanded) => `${m}$self.shouldRenderContents(arguments[0],${isExpanded})?null:` }, + // Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar { - // Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always, - match: /\.isExpanded\),.{0,30}children:\[/, + match: /\.isExpanded].{0,110}children:\[/, replace: "$&$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)&&" }, + // Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar { - // Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always, - match: /(?<=\.expandedFolderBackground.+?}\),)(?=\i,)/, + match: /(?<=\.folderGroupBackground.*?}\),)(?=\i,)/, replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)?null:" - }, - { - // Discord adds a slight bottom margin of 4px when it's expanded - // Which looks off when there's nothing open in the folder - predicate: () => !settings.store.keepIcons, - match: /(?=className:.{0,50}folderIcon)/, - replace: "style:arguments[0]?.isBetterFolders?{}:{marginBottom:0}," } ] }, @@ -340,7 +345,6 @@ export default definePlugin({ makeGuildsBarTreeFilter(isBetterFolders: boolean) { return child => { if (!isBetterFolders) return true; - return !!child?.props?.renderTreeNode; }; }, @@ -360,6 +364,14 @@ export default definePlugin({ } }, + makeNewButtonFilter(isBetterFolders: boolean) { + return child => { + if (!isBetterFolders) return true; + + return !child?.props?.barClassName; + }; + }, + shouldShowTransition(props: any) { // Pending guilds if (props?.folderNode?.id === 1) return true; diff --git a/src/plugins/plainFolderIcon/index.ts b/src/plugins/plainFolderIcon/index.ts index bb6876b5..5870d7e6 100644 --- a/src/plugins/plainFolderIcon/index.ts +++ b/src/plugins/plainFolderIcon/index.ts @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +import "./styles.css"; + import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; @@ -23,21 +25,18 @@ export default definePlugin({ name: "PlainFolderIcon", description: "Doesn't show the small guild icons in folders", authors: [Devs.botato], + + folderClassName: "vc-plainFolderIcon-plain", + patches: [{ - find: ".expandedFolderIconWrapper", + find: ".folderPreviewGuildIconError", replacement: [ - // there are two elements, the first one is the plain folder icon - // the second is the four guild preview icons - // always show this one (the plain icons) { - match: /\(\i\|\|\i\)&&(\(.{0,40}\(\i\.animated)/, - replace: "$1", - }, - // and never show this one (the guild preview icons) - { - match: /\(\i\|\|!\i\)&&(\(.{0,40}\(\i\.animated)/, - replace: "false&&$1", + // Discord always renders both and uses a css transtion to switch bewteen them + match: /.folderButtonContent]:(!\i)/, + replace: (m, cond) => `${m},[$self.folderClassName]:${cond}` } + ] }] }); diff --git a/src/plugins/plainFolderIcon/styles.css b/src/plugins/plainFolderIcon/styles.css new file mode 100644 index 00000000..f832b404 --- /dev/null +++ b/src/plugins/plainFolderIcon/styles.css @@ -0,0 +1,12 @@ +.vc-plainFolderIcon-plain { + /* Without this, this are a bit laggier */ + transition: none !important; + + /* Don't show the mini guild icons */ + transform: none; +} + +.vc-plainFolderIcon-plain svg { + width: 35px; + height: 35px; +} diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index 7a61821b..f45da089 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -76,6 +76,7 @@ export default definePlugin({ }, patches: [ + // In the user popout. eg: when clicking the name in chat { find: ".POPOUT,user:", replacement: { @@ -90,8 +91,8 @@ export default definePlugin({ replace: "$1$self.BiteSizeReviewsButton({user:arguments[0].user}),$2" } }, + // places like the user profile on the right in dms { - // places like the user profile on the right in dms find: 'location:"UserProfileSiebar"', replacement: { match: /(children:\[)([^[]+shouldShowTooltip:)/, diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 21598633..3c55295c 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -357,7 +357,7 @@ export default definePlugin({ ] }, { - find: "overflowCountVariant", + find: ".SIZE_24,overflowCountVariant:", replacement: [ { // Create a variable for the channel prop From 385b58c7ffae6b6d91b2251239bfea5ad482be16 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 30 Apr 2025 23:01:36 -0400 Subject: [PATCH 080/198] Fixes For MutualGroupDMs & ServerInfo --- src/plugins/mutualGroupDMs/index.tsx | 23 ++++++++++++----------- src/plugins/serverInfo/styles.css | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 3c8e30fe..42f4f2e3 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -32,8 +32,8 @@ const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); const UserUtils = findByPropsLazy("getGlobalName"); const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds"); +const MutualsListClasses = findByPropsLazy("row", "icon", "name", "nick"); const ExpandableList = findComponentByCodeLazy('"PRESS_SECTION"', ".header"); -const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon"); function getGroupDMName(channel: Channel) { return channel.name || @@ -59,21 +59,22 @@ function renderClickableGDMs(mutualDms: Channel[], onClose: () => void) { return mutualDms.map(c => ( { onClose(); SelectedChannelActionCreators.selectPrivateChannel(c.id); }} > - - -
-
{getGroupDMName(c)}
-
{c.recipients.length + 1} Members
+
+ + +
+
{getGroupDMName(c)}
+
{c.recipients.length + 1} Members
+
)); diff --git a/src/plugins/serverInfo/styles.css b/src/plugins/serverInfo/styles.css index a77605e9..9b251f3d 100644 --- a/src/plugins/serverInfo/styles.css +++ b/src/plugins/serverInfo/styles.css @@ -41,9 +41,9 @@ border-bottom: 2px solid var(--background-modifier-accent); margin: 20px 12px 0; display: flex; - gap: 40px; align-items: stretch; flex-direction: row; + justify-content: center; } .vc-gp-tab { From 0d09c083c6a5089c7a81a9b9a064877985c8cb06 Mon Sep 17 00:00:00 2001 From: Reycko <78082869+Reycko@users.noreply.github.com> Date: Thu, 1 May 2025 18:29:47 +0200 Subject: [PATCH 081/198] plugin: SplitLargeMessages (#249) * add(SplitLargeMessages) * Fixes * Update README.md --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> --- README.md | 5 +- .../splitLargeMessages/index.ts | 156 ++++++++++++++++++ src/utils/constants.ts | 4 + 3 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 src/equicordplugins/splitLargeMessages/index.ts diff --git a/README.md b/README.md index e8af6a0e..946a646e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-169 additional plugins +170 additional plugins ### All Platforms @@ -96,6 +96,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - JumpToStart by Samwich - KeyboardSounds by HypedDomi - KeywordNotify by camila314 & x3rt +- - LastActive by Crxa - LimitMiddleClickPaste by no dev listed - LoginWithQR by nexpid - MediaPlaybackSpeed by D3SOX @@ -142,6 +143,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - Signature by Ven, Rini, ImBanana, KrystalSkull - Slap by Korbo - SoundBoardLogger by Moxxie, fres, echo, maintained by thororen +- - SplitLargeMessages by Reycko - SpotifyLyrics by Joona - StatsfmPresence by Crxa - StatusPresets by iamme @@ -177,7 +179,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - Woof by Samwich - WriteUpperCase by Samwich & KrystalSkull - YoutubeDescription by arHSM -- LastActive by Crxa ### Web Only diff --git a/src/equicordplugins/splitLargeMessages/index.ts b/src/equicordplugins/splitLargeMessages/index.ts new file mode 100644 index 00000000..88b4e426 --- /dev/null +++ b/src/equicordplugins/splitLargeMessages/index.ts @@ -0,0 +1,156 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { addMessagePreSendListener, MessageSendListener, removeMessagePreSendListener } from "@api/MessageEvents"; +import { definePluginSettings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import { getCurrentChannel, sendMessage } from "@utils/discord"; +import definePlugin, { OptionType } from "@utils/types"; +import { ChannelStore, ComponentDispatch, PermissionsBits, UserStore } from "@webpack/common"; + +let maxLength: number = 0; + +const canSplit: () => boolean = () => { + const slowmode = getCurrentChannel()?.rateLimitPerUser ?? 0; + return (settings.store.splitInSlowmode ? slowmode < settings.store.slowmodeMax : slowmode <= 0) && settings.store.disableFileConversion; +}; + +const autoMaxLength = () => { + const hasNitro = UserStore.getCurrentUser().premiumType === 2; + return hasNitro ? 4000 : 2000; +}; + +const split = async (channelId: string, chunks: string[], delayInMs: number) => { + const sendChunk = async (chunk: string) => { + await sendMessage(channelId, { content: chunk }, true); + }; + + // Send the chunks + for (let i = 0; i < chunks.length; i++) { + await sendChunk(chunks[i]); + if (i < chunks.length - 1) // Not the last chunk + await new Promise(resolve => setTimeout(resolve, delayInMs)); // Wait for `delayInMs` + } +}; + +const listener: MessageSendListener = async (channelId, msg) => { + if (msg.content.trim().length < maxLength || !canSplit()) return; // Nothing to split + + const channel = ChannelStore.getChannel(channelId); + + // Check for slowmode + let isSlowmode = channel.rateLimitPerUser > 0; + if ((channel.accessPermissions & PermissionsBits.MANAGE_MESSAGES) === PermissionsBits.MANAGE_MESSAGES + || (channel.accessPermissions & PermissionsBits.MANAGE_CHANNELS) === PermissionsBits.MANAGE_CHANNELS) + isSlowmode = false; + + // Not slowmode or splitInSlowmode is on and less than slowmodeMax + if (!isSlowmode || (settings.store.splitInSlowmode && channel.rateLimitPerUser < settings.store.slowmodeMax)) { + const chunks: string[] = []; + const { hardSplit } = settings.store; + while (msg.content.length > maxLength) { + msg.content = msg.content.trim(); + + // Get last space or newline + const splitIndex = Math.max(msg.content.lastIndexOf(" ", maxLength), msg.content.lastIndexOf("\n", maxLength)); + + // If hard split is on or neither newline or space found, split at maxLength + if (hardSplit || splitIndex === -1) { + chunks.push(msg.content.slice(0, maxLength)); + msg.content = msg.content.slice(maxLength); + } + else { + chunks.push(msg.content.slice(0, splitIndex)); + msg.content = msg.content.slice(splitIndex); + } + } + + ComponentDispatch.dispatchToLastSubscribed("CLEAR_TEXT"); + await split(channelId, [...chunks, msg.content], settings.store.sendDelay * 1000); + } + return { cancel: true }; +}; + +const settings = definePluginSettings({ + maxLength: { + type: OptionType.NUMBER, + description: "Maximum length of a message before it is split. Set to 0 to automatically detect.", + default: 0, + max: 4000, + onChange(newValue) { + if (newValue === 0) + maxLength = autoMaxLength(); + }, + }, + + disableFileConversion: { + type: OptionType.BOOLEAN, + description: "If true, disables file conversion for large messages.", + default: true, + }, + + sendDelay: { + type: OptionType.SLIDER, + description: "Delay between each chunk in seconds.", + default: 1, + markers: [1, 2, 3, 5, 10], + }, + + hardSplit: { + type: OptionType.BOOLEAN, + description: "If true, splits on the last character instead of the last space/newline.", + default: false, + }, + + splitInSlowmode: { + type: OptionType.BOOLEAN, + description: "Should messages be split if the channel has slowmode enabled?", + }, + + slowmodeMax: { + type: OptionType.NUMBER, + description: "Maximum slowmode time if splitting in slowmode.", + default: 5, + min: 1, + max: 30, + } +}); + +export default definePlugin({ + name: "SplitLargeMessages", + description: "Splits large messages into multiple to fit Discord's message limit.", + authors: [EquicordDevs.Reycko], + dependencies: ["MessageEventsAPI"], + settings, + + start() { + if (settings.store.maxLength === 0) + maxLength = autoMaxLength(); + addMessagePreSendListener(listener); + }, + + stop() { + removeMessagePreSendListener(listener); + }, + + patches: [ + { + find: 'type:"MESSAGE_LENGTH_UPSELL"', // bypass message length check + replacement: { + match: /if\(\i.length>\i/, + replace: "if(false", + } + }, + + { + find: '(this,"hideAutocomplete"', // disable file conversion + replacement: { + match: /if\(\i.length>\i\)/, + replace: "if(false)", + }, + } + ] +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 6baba55e..b798c88e 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1058,6 +1058,10 @@ export const EquicordDevs = Object.freeze({ name: "Buzzy", id: 1273353654644117585n }, + Reycko: { + name: "Reycko", + id: 1123725368004726794n, + } } satisfies Record); // iife so #__PURE__ works correctly From efd1821fadd7e22f3e1c9e928203324aaf1637e0 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 1 May 2025 14:09:40 -0400 Subject: [PATCH 082/198] ShowMeYourName Extra Settings & Fix WhosWatching --- src/equicordplugins/randomVoice/index.tsx | 26 ++++++------ src/equicordplugins/whosWatching/index.tsx | 5 +-- src/plugins/showMeYourName/index.tsx | 46 ++++++++++++++++++---- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/equicordplugins/randomVoice/index.tsx b/src/equicordplugins/randomVoice/index.tsx index 72ecae7e..96ea92f9 100644 --- a/src/equicordplugins/randomVoice/index.tsx +++ b/src/equicordplugins/randomVoice/index.tsx @@ -11,8 +11,8 @@ import { makeRange } from "@components/PluginSettings/components"; import { debounce } from "@shared/debounce"; import { EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy, findByCode, findByProps } from "@webpack"; -import { ChannelStore, ContextMenuApi, GuildStore, IconUtils, Menu, ChannelRouter, PermissionStore, React, SelectedChannelStore, PermissionsBits, Toasts, UserStore } from "@webpack/common"; +import { findByCode, findByProps, findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; +import { ChannelRouter, ChannelStore, ContextMenuApi, GuildStore, Menu, PermissionsBits, PermissionStore, React, SelectedChannelStore, Toasts, UserStore } from "@webpack/common"; import style from "./styles.css?managed"; @@ -86,10 +86,10 @@ const settings = definePluginSettings({ description: "Automatically turns on camera", default: false, }, - autoStream: { - type: OptionType.BOOLEAN, + autoStream: { + type: OptionType.BOOLEAN, description: "Automatically turns on stream", - default: false, + default: false, }, selfMute: { type: OptionType.BOOLEAN, @@ -101,10 +101,10 @@ const settings = definePluginSettings({ description: "Automatically deafems your mic when joining voice-channel.", default: false, }, - leaveEmpty: { + leaveEmpty: { type: OptionType.BOOLEAN, description: "Finds a random-call, when the voice chat is empty.", - default: false, + default: false, }, avoidStages: { type: OptionType.BOOLEAN, @@ -177,21 +177,21 @@ export default definePlugin({ } ], flux: { - VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[] }) { + VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) { const currentUserId = UserStore.getCurrentUser().id; const myChannelId = VoiceStateStore.getVoiceStateForUser(currentUserId)?.channelId; if (!myChannelId || !settings.store.leaveEmpty) return; - + const voiceStatesMap = VoiceStateStore.getVoiceStates() as Record; const othersInChannel = Object.values(voiceStatesMap).filter(vs => vs.channelId === myChannelId && vs.userId !== currentUserId ); - + if (othersInChannel.length === 0) { - randomVoice() + randomVoice(); } }, - }, + }, start() { enableStyle(style); }, @@ -261,7 +261,7 @@ function ContextMenu() { aria-label="Voice state modifier" > - + settings.store.showPanel, find: "this.renderEmbeddedActivity()", replacement: { - match: /(?<=children.{0,50})"div"(?=.{0,500}this\.renderEmbeddedActivity\(\))/, + match: /(?<=render\(\).{0,500}children.{0,50})"div"(?=.{0,500}this\.renderEmbeddedActivity\(\))/, replace: "$self.WrapperComponent" } } @@ -118,7 +117,7 @@ export default definePlugin({ return ( <>
{props.children}
-
+
{getIntlMessage("SPECTATORS", { numViewers: userIds.length })} diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index f167287f..3efe60e0 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -45,9 +45,26 @@ const settings = definePluginSettings({ type: OptionType.BOOLEAN, default: false, description: "Use friend names in place of usernames (overrides Display Names option if applicable)" - } + }, + memberList: { + type: OptionType.BOOLEAN, + default: true, + description: "Show usernames in member list", + }, + voiceChannelList: { + type: OptionType.BOOLEAN, + default: true, + description: "Show usernames in voice channel list", + }, }); +function getUsername(user: any): string { + const friendName = RelationshipStore.getNickname(user.id); + if (settings.store.preferFriend && friendName) return friendName; + if (settings.store.displayNames) return user.globalName || user.username; + return user.username; +} + export default definePlugin({ name: "ShowMeYourName", description: "Display usernames next to nicks, or no nicks at all", @@ -61,18 +78,31 @@ export default definePlugin({ replace: "$self.renderUsername(arguments[0])" } }, + { + find: "._areActivitiesExperimentallyHidden=(", + replacement: { + match: /(?<=user:(\i),currentUser:\i,nick:)\i/, + replace: "$self.getUsername($1)" + }, + predicate: () => settings.store.memberList + }, + { + find: ".usernameSpeaking]", + predicate: () => settings.store.voiceChannelList, + replacement: [ + { + match: /(?<=children:\[null!=\i\?)\i(?=:\i\.\i\.getName\((\i)\))/, + replace: "$self.getUsername($1)" + }, + ] + }, ], settings, - + getUsername, renderUsername: ErrorBoundary.wrap(({ author, message, isRepliedMessage, withMentionPrefix, userOverride }: UsernameProps) => { try { const user = userOverride ?? message.author; - const friendName = RelationshipStore.getNickname(user.id); - let { username } = user; - if (settings.store.displayNames) - username = (user as any).globalName || username; - if (settings.store.preferFriend) - username = friendName ?? username; + const username = getUsername(user); const { nick } = author; const prefix = withMentionPrefix ? "@" : ""; From 1e02050bb37b432077965a3df706ad0b7e5a3e0b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 1 May 2025 14:30:56 -0400 Subject: [PATCH 083/198] ShowMeYourName Add EmojiReactions Setting --- src/plugins/showMeYourName/index.tsx | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index 3efe60e0..b89e865f 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -48,14 +48,19 @@ const settings = definePluginSettings({ }, memberList: { type: OptionType.BOOLEAN, - default: true, + default: false, description: "Show usernames in member list", }, voiceChannelList: { type: OptionType.BOOLEAN, - default: true, + default: false, description: "Show usernames in voice channel list", }, + emojiReactions: { + type: OptionType.BOOLEAN, + default: false, + description: "Show usernames in emoji reactions", + }, }); function getUsername(user: any): string { @@ -80,11 +85,11 @@ export default definePlugin({ }, { find: "._areActivitiesExperimentallyHidden=(", + predicate: () => settings.store.memberList, replacement: { match: /(?<=user:(\i),currentUser:\i,nick:)\i/, replace: "$self.getUsername($1)" }, - predicate: () => settings.store.memberList }, { find: ".usernameSpeaking]", @@ -96,6 +101,16 @@ export default definePlugin({ }, ] }, + { + find: "#{intl::REACTION_TOOLTIP_1}", + predicate: () => settings.store.emojiReactions, + replacement: [ + { + match: /\i\.\i\.getName\(\i,null==.{0,15},(\i)\)/g, + replace: "$self.getUsername($1)," + }, + ] + }, ], settings, getUsername, From b0f12a9e3fe7acfde4b27f2468ae38a92b53bbd1 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 1 May 2025 14:58:40 -0400 Subject: [PATCH 084/198] Fix ShowMeYourName Settings --- src/plugins/showMeYourName/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index b89e865f..be92af83 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -50,16 +50,19 @@ const settings = definePluginSettings({ type: OptionType.BOOLEAN, default: false, description: "Show usernames in member list", + restartNeeded: true }, voiceChannelList: { type: OptionType.BOOLEAN, default: false, description: "Show usernames in voice channel list", + restartNeeded: true }, emojiReactions: { type: OptionType.BOOLEAN, default: false, description: "Show usernames in emoji reactions", + restartNeeded: true }, }); From eb12516344bf6878946c22a7c825370d1b146f8e Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 1 May 2025 15:19:16 -0400 Subject: [PATCH 085/198] Misc Fixes --- src/plugins/showMeYourName/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index be92af83..37703bd4 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -99,7 +99,7 @@ export default definePlugin({ predicate: () => settings.store.voiceChannelList, replacement: [ { - match: /(?<=children:\[null!=\i\?)\i(?=:\i\.\i\.getName\((\i)\))/, + match: /(?<=children:\[)null!=\i\?\i:\i\.\i\.getName\((\i)\)/, replace: "$self.getUsername($1)" }, ] @@ -110,7 +110,7 @@ export default definePlugin({ replacement: [ { match: /\i\.\i\.getName\(\i,null==.{0,15},(\i)\)/g, - replace: "$self.getUsername($1)," + replace: "$self.getUsername($1)" }, ] }, From 7d213a638c1f41fb89962ee2ecbcf1ed43d09ce2 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 1 May 2025 23:55:01 -0400 Subject: [PATCH 086/198] ShowMeYourName Guild Nicknames --- src/plugins/showMeYourName/index.tsx | 29 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index 37703bd4..7011b06b 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -10,7 +10,7 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { RelationshipStore } from "@webpack/common"; +import { GuildMemberStore, RelationshipStore } from "@webpack/common"; import { Message, User } from "discord-types/general"; interface UsernameProps { @@ -19,6 +19,7 @@ interface UsernameProps { withMentionPrefix?: boolean; isRepliedMessage: boolean; userOverride?: User; + guildId: string; } const settings = definePluginSettings({ @@ -28,6 +29,7 @@ const settings = definePluginSettings({ options: [ { label: "Username then nickname", value: "user-nick", default: true }, { label: "Nickname then username", value: "nick-user" }, + { label: "Nickname only", value: "nick" }, { label: "Username only", value: "user" }, ], }, @@ -66,9 +68,12 @@ const settings = definePluginSettings({ }, }); -function getUsername(user: any): string { +function getUsername(user: any, guildId: string): string { const friendName = RelationshipStore.getNickname(user.id); + const guildNick = GuildMemberStore.getNick(guildId, user.id); + if (settings.store.preferFriend && friendName) return friendName; + if (settings.store.mode === "nick" && guildNick) return guildNick; if (settings.store.displayNames) return user.globalName || user.username; return user.username; } @@ -82,16 +87,16 @@ export default definePlugin({ find: '"BaseUsername"', replacement: { /* TODO: remove \i+\i once change makes it to stable */ - match: /(?<=onContextMenu:\i,children:)(?:\i\+\i|\i)/, - replace: "$self.renderUsername(arguments[0])" + match: /(?<=onContextMenu:\i,children:)(?:\i\+\i|\i)(?=.*?contextGuildId:(\i))/, + replace: "$self.renderUsername({ ...arguments[0], guildId: $1 })", } }, { find: "._areActivitiesExperimentallyHidden=(", predicate: () => settings.store.memberList, replacement: { - match: /(?<=user:(\i),currentUser:\i,nick:)\i/, - replace: "$self.getUsername($1)" + match: /(?<=user:(\i),currentUser:\i,nick:)\i(?=.*?guildId:(\i))/, + replace: "$self.getUsername($1,$2)" }, }, { @@ -99,8 +104,8 @@ export default definePlugin({ predicate: () => settings.store.voiceChannelList, replacement: [ { - match: /(?<=children:\[)null!=\i\?\i:\i\.\i\.getName\((\i)\)/, - replace: "$self.getUsername($1)" + match: /(?<=children:\[)null!=\i\?\i:\i\.\i\.getName\((\i)\)(?=.*?contextGuildId:(\i))/, + replace: "$self.getUsername($1,$2)" }, ] }, @@ -109,18 +114,18 @@ export default definePlugin({ predicate: () => settings.store.emojiReactions, replacement: [ { - match: /\i\.\i\.getName\(\i,null==.{0,15},(\i)\)/g, - replace: "$self.getUsername($1)" + match: /\i\.\i\.getName\((\i),null==.{0,15},(\i)\)/g, + replace: "$self.getUsername($2,$1)" }, ] }, ], settings, getUsername, - renderUsername: ErrorBoundary.wrap(({ author, message, isRepliedMessage, withMentionPrefix, userOverride }: UsernameProps) => { + renderUsername: ErrorBoundary.wrap(({ author, message, isRepliedMessage, withMentionPrefix, userOverride, guildId }: UsernameProps) => { try { const user = userOverride ?? message.author; - const username = getUsername(user); + const username = getUsername(user, guildId); const { nick } = author; const prefix = withMentionPrefix ? "@" : ""; From 61e75e1d8964745d4b87419f8591aeb5623af36d Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 2 May 2025 12:03:13 -0400 Subject: [PATCH 087/198] Fix Invite URL --- src/plugins/_core/supportHelper.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index 3b299b40..fa09561b 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -216,7 +216,7 @@ export default definePlugin({
, confirmText: "Go to Equicord Support", cancelText: "Okay continue", - onConfirm: () => VencordNative.native.openExternal("https://discord.gg/equicord"), + onConfirm: () => VencordNative.native.openExternal("https://discord.gg/5Xh2W87egW"), }); } @@ -251,7 +251,7 @@ export default definePlugin({ body:
You are using an externally updated Equicord version, the ability to help you here may be limited. - Please join the Equicord Server for support, + Please join the Equicord Server for support, or if this issue persists on Vencord, continue on.
From 838a90831f0b0405150cc69e9ee0ca11d96f5c59 Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Fri, 2 May 2025 20:40:07 -0400 Subject: [PATCH 088/198] Fix BetterFolders, ReviewDB and minor ShowHiddenChannels patch (#3396) Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> --- src/plugins/betterFolders/index.tsx | 101 ++++++++++++++--------- src/plugins/reviewDB/index.tsx | 12 +-- src/plugins/showHiddenChannels/index.tsx | 17 ++-- 3 files changed, 80 insertions(+), 50 deletions(-) diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 3bcf0335..2b1defd8 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -50,6 +50,35 @@ function closeFolders() { FolderUtils.toggleGuildFolderExpand(id); } +// Nuckyz: Unsure if this should be a general utility or not +function filterTreeWithTargetNode(children: any, predicate: (node: any) => boolean) { + if (children == null) { + return false; + } + + if (!Array.isArray(children)) { + if (predicate(children)) { + return true; + } + + return filterTreeWithTargetNode(children.props.children, predicate); + } + + + let childIsTargetChild = false; + for (let i = 0; i < children.length; i++) { + const shouldKeep = filterTreeWithTargetNode(children[i], predicate); + if (shouldKeep) { + childIsTargetChild = true; + continue; + } + + children.splice(i--, 1); + } + + return childIsTargetChild; +} + export const settings = definePluginSettings({ sidebar: { type: OptionType.BOOLEAN, @@ -114,29 +143,35 @@ export default definePlugin({ predicate: () => settings.store.sidebar, replacement: [ // Create the isBetterFolders variable in the GuildsBar component + // Needed because we access this from a non-arrow closure so we can't use arguments[0] { match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i,)/, replace: "$&,isBetterFolders" }, + // Export the isBetterFolders and betterFoldersExpandedIds variable to the Guild List component + { + match: /0,\i\.jsxs?[^0}]{0,100}guildDiscoveryButton:\i,/g, + replace: "$&isBetterFolders:arguments[0]?.isBetterFolders,betterFoldersExpandedIds:arguments[0]?.betterFoldersExpandedIds," + }, + // Export the isBetterFolders variable to the folders component + { + match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/, + replace: '$&isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,' + }, // If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders { match: /\[(\i)\]=(\(0,\i\.\i\).{0,40}getGuildsTree\(\).+?}\))(?=,)/, replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0]?.isBetterFolders,betterFoldersOriginalTree,arguments[0]?.betterFoldersExpandedIds)` }, - // If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children + // If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the Guild List children { match: /lastTargetNode:\i\[\i\.length-1\].+?}\)(?::null)?\](?=}\))/, replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0]?.isBetterFolders))" }, - // If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children + // If we are rendering the Better Folders sidebar, we filter out everything but the Guild List from the Sidebar children { - match: /unreadMentionsIndicatorBottom,.+?}\)\]/, - replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0]?.isBetterFolders))" - }, - // Export the isBetterFolders variable to the folders component - { - match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/, - replace: '$&isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,' + match: /unreadMentionsFixedFooter\].+?\]/, + replace: "$&.filter($self.makeGuildsBarSidebarFilter(!!arguments[0]?.isBetterFolders))" } ] }, @@ -161,7 +196,7 @@ export default definePlugin({ ] }, { - find: ".expandedFolderBackground,", + find: ".FOLDER_ITEM_ANIMATION_DURATION),", predicate: () => settings.store.sidebar, replacement: [ // We use arguments[0] to access the isBetterFolders variable in this nested folder component (the parent exports all the props so we don't have to patch it) @@ -181,27 +216,20 @@ export default definePlugin({ // If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded { predicate: () => !settings.store.keepIcons, - match: /expandedFolderBackground,.+?,(?=\i\(\(\i,\i,\i\)=>{let{key.{0,45}ul)(?<=selected:\i,expanded:(\i),.+?)/, + match: /folderGroupBackground.+?,(?=\i\(\(\i,\i,\i\)=>{let{key:.{0,70}"ul")(?<=selected:\i,expanded:(\i),.+?)/, replace: (m, isExpanded) => `${m}$self.shouldRenderContents(arguments[0],${isExpanded})?null:` }, + // Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar { - // Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always, - match: /\.isExpanded\),.{0,30}children:\[/, + match: /\.isExpanded\].{0,110}children:\[/, replace: "$&$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)&&" }, + // Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar { - // Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always, - match: /(?<=\.expandedFolderBackground.+?}\),)(?=\i,)/, + match: /(?<=\.folderGroupBackground.*?}\),)(?=\i,)/, replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)?null:" - }, - { - // Discord adds a slight bottom margin of 4px when it's expanded - // Which looks off when there's nothing open in the folder - predicate: () => !settings.store.keepIcons, - match: /(?=className:.{0,50}folderIcon)/, - replace: "style:arguments[0]?.isBetterFolders?{}:{marginBottom:0}," } ] }, @@ -278,6 +306,9 @@ export default definePlugin({ } }, + FolderSideBar, + closeFolders, + gridStyle: "vc-betterFolders-sidebar-grid", getGuildTree(isBetterFolders: boolean, originalTree: any, expandedFolderIds?: Set) { @@ -299,34 +330,33 @@ export default definePlugin({ makeGuildsBarGuildListFilter(isBetterFolders: boolean) { return child => { - if (!isBetterFolders) return true; + if (!isBetterFolders) { + return true; + } try { return child?.props?.["aria-label"] === getIntlMessage("SERVERS"); } catch (e) { console.error(e); + return true; } - - return true; }; }, - makeGuildsBarTreeFilter(isBetterFolders: boolean) { + makeGuildsBarSidebarFilter(isBetterFolders: boolean) { return child => { - if (!isBetterFolders) return true; - - if (child?.props?.className?.includes("itemsContainer") && child.props.children != null) { - // Filter out everything but the scroller for the guild list - child.props.children = child.props.children.filter(child => child?.props?.onScroll != null); + if (!isBetterFolders) { return true; } - return false; + return filterTreeWithTargetNode(child, child => child?.props?.renderTreeNode != null); }; }, shouldShowFolderIconAndBackground(isBetterFolders: boolean, expandedFolderIds?: Set) { - if (!isBetterFolders) return true; + if (!isBetterFolders) { + return true; + } switch (settings.store.showFolderIcon) { case FolderIconDisplay.Never: @@ -352,8 +382,5 @@ export default definePlugin({ if (props?.folderNode?.id === 1) return false; return !props?.isBetterFolders && isExpanded; - }, - - FolderSideBar, - closeFolders, + } }); diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index 822ebde6..32546b9b 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -77,23 +77,23 @@ export default definePlugin({ patches: [ { - find: ".BITE_SIZE,user:", + find: ".POPOUT,user:", replacement: { - match: /{profileType:\i\.\i\.BITE_SIZE,children:\[/, + match: /children:\[(?=[^[]+?shouldShowTooltip:)/, replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," } }, { - find: ".FULL_SIZE,user:", + find: ".MODAL,user:", replacement: { - match: /{profileType:\i\.\i\.FULL_SIZE,children:\[/, + match: /children:\[(?=[^[]+?shouldShowTooltip:)/, replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," } }, { - find: 'location:"UserProfilePanel"', + find: ".SIDEBAR,shouldShowTooltip:", replacement: { - match: /{profileType:\i\.\i\.PANEL,children:\[/, + match: /children:\[(?=[^[]+?shouldShowTooltip:)/, replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," } } diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 7a38bb12..7a3dd9fb 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -325,7 +325,7 @@ export default definePlugin({ ] }, { - find: '})},"overflow"))', + find: '="interactive-normal",overflowCountClassName:', replacement: [ { // Create a variable for the channel prop @@ -334,18 +334,21 @@ export default definePlugin({ }, { // Make Discord always render the plus button if the component is used inside the HiddenChannelLockScreen - match: /\i>0(?=&&.{0,60}renderPopout)/, + match: /\i>0(?=&&.{0,30}Math.min)/, replace: m => `($self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)?true:${m})` }, { - // Prevent Discord from overwriting the last children with the plus button if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen - match: /(?<=\.value\(\),(\i)=.+?length-)1(?=\]=.{0,60}renderPopout)/, + // Prevent Discord from overwriting the last children with the plus button + // if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen + match: /(?<=\i\.length-)1(?=\]=.{0,60}renderPopout)(?<=(\i)=\i\.length-\i.+?)/, replace: (_, amount) => `($self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)&&${amount}<=0?0:1)` }, { - // Show only the plus text without overflowed children amount if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen - match: /(?<="\+",)(\i)\+1/, - replace: (m, amount) => `$self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)&&${amount}<=0?"":${m}` + // Show only the plus text without overflowed children amount + // if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen + match: /(?<="\+"\.concat\()\i/, + replace: overflowTextAmount => "" + + `$self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)&&(${overflowTextAmount}-1)<=0?"":${overflowTextAmount}` } ] }, From 2613ec170ef7000a95e165bab807084b864c475f Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 2 May 2025 21:00:56 -0400 Subject: [PATCH 089/198] MoreUserTags Chat --- README.md | 3 +- src/api/NicknameIcons.tsx | 40 +++ src/api/index.ts | 6 + src/equicordplugins/moreUserTags/consts.ts | 63 ++++ src/equicordplugins/moreUserTags/index.tsx | 183 ++++++++++ src/equicordplugins/moreUserTags/settings.tsx | 81 +++++ src/equicordplugins/moreUserTags/styles.css | 20 ++ src/equicordplugins/moreUserTags/types.ts | 32 ++ .../components/NotificationComponent.tsx | 9 +- src/plugins/_api/nicknameIcons.ts | 23 ++ src/plugins/index.ts | 10 +- src/plugins/platformIndicators/index.tsx | 325 +++++++++--------- src/plugins/platformIndicators/style.css | 18 +- src/plugins/userVoiceShow/components.tsx | 40 ++- src/plugins/userVoiceShow/index.tsx | 37 +- src/utils/constants.ts | 4 + src/utils/types.ts | 2 + 17 files changed, 668 insertions(+), 228 deletions(-) create mode 100644 src/api/NicknameIcons.tsx create mode 100644 src/equicordplugins/moreUserTags/consts.ts create mode 100644 src/equicordplugins/moreUserTags/index.tsx create mode 100644 src/equicordplugins/moreUserTags/settings.tsx create mode 100644 src/equicordplugins/moreUserTags/styles.css create mode 100644 src/equicordplugins/moreUserTags/types.ts create mode 100644 src/plugins/_api/nicknameIcons.ts diff --git a/README.md b/README.md index 946a646e..5e77a949 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-170 additional plugins +171 additional plugins ### All Platforms @@ -108,6 +108,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - MessageTranslate by Samwich - ModalFade by Kyuuhachi - MoreStickers by Leko & Arjix +- MoreUserTags by Cyn, TheSun, RyanCaoDev, LordElias, AutumnVN, hen - Morse by zyqunix - NeverPausePreviews by vappstar - NewPluginsManager by Sqaaakoi diff --git a/src/api/NicknameIcons.tsx b/src/api/NicknameIcons.tsx new file mode 100644 index 00000000..8b0fbc20 --- /dev/null +++ b/src/api/NicknameIcons.tsx @@ -0,0 +1,40 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import ErrorBoundary from "@components/ErrorBoundary"; +import { Logger } from "@utils/Logger"; +import { ReactNode } from "react"; + +export interface NicknameIconProps { + userId: string; +} + +export type NicknameIconFactory = (props: NicknameIconProps) => ReactNode | Promise; + +export interface NicknameIcon { + priority: number; + factory: NicknameIconFactory; +} + +const nicknameIcons = new Map(); +const logger = new Logger("NicknameIcons"); + +export function addNicknameIcon(id: string, factory: NicknameIconFactory, priority = 0) { + return nicknameIcons.set(id, { + priority, + factory: ErrorBoundary.wrap(factory, { noop: true, onError: error => logger.error(`Failed to render ${id}`, error) }) + }); +} + +export function removeNicknameIcon(id: string) { + return nicknameIcons.delete(id); +} + +export function _renderIcons(props: NicknameIconProps) { + return Array.from(nicknameIcons) + .sort((a, b) => b[1].priority - a[1].priority) + .map(([id, { factory: NicknameIcon }]) => ); +} diff --git a/src/api/index.ts b/src/api/index.ts index f0538f52..8af6a49f 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -27,6 +27,7 @@ import * as $MessageDecorations from "./MessageDecorations"; import * as $MessageEventsAPI from "./MessageEvents"; import * as $MessagePopover from "./MessagePopover"; import * as $MessageUpdater from "./MessageUpdater"; +import * as $NicknameIcons from "./NicknameIcons"; import * as $Notices from "./Notices"; import * as $Notifications from "./Notifications"; import * as $ServerList from "./ServerList"; @@ -123,6 +124,11 @@ export const MessageUpdater = $MessageUpdater; */ export const UserSettings = $UserSettings; +/** + * An API allowing you to add icons to the nickname, in profiles + */ +export const NicknameIcons = $NicknameIcons; + /** * Just used to identify if user is on Equicord as Vencord doesnt have this */ diff --git a/src/equicordplugins/moreUserTags/consts.ts b/src/equicordplugins/moreUserTags/consts.ts new file mode 100644 index 00000000..14ea3dd9 --- /dev/null +++ b/src/equicordplugins/moreUserTags/consts.ts @@ -0,0 +1,63 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { findByCodeLazy, findLazy } from "@webpack"; +import { GuildStore } from "@webpack/common"; +import { RC } from "@webpack/types"; +import { Channel, Guild, Message, User } from "discord-types/general"; + +import type { ITag } from "./types"; + +export const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot(); +export const tags = [ + { + name: "WEBHOOK", + displayName: "Webhook", + description: "Messages sent by webhooks", + condition: isWebhook + }, { + name: "OWNER", + displayName: "Owner", + description: "Owns the server", + condition: (_, user, channel) => GuildStore.getGuild(channel?.guild_id)?.ownerId === user.id + }, { + name: "ADMINISTRATOR", + displayName: "Admin", + description: "Has the administrator permission", + permissions: ["ADMINISTRATOR"] + }, { + name: "MODERATOR_STAFF", + displayName: "Staff", + description: "Can manage the server, channels or roles", + permissions: ["MANAGE_GUILD", "MANAGE_CHANNELS", "MANAGE_ROLES"] + }, { + name: "MODERATOR", + displayName: "Mod", + description: "Can manage messages or kick/ban people", + permissions: ["MANAGE_MESSAGES", "KICK_MEMBERS", "BAN_MEMBERS"] + }, { + name: "VOICE_MODERATOR", + displayName: "VC Mod", + description: "Can manage voice chats", + permissions: ["MOVE_MEMBERS", "MUTE_MEMBERS", "DEAFEN_MEMBERS"] + }, { + name: "CHAT_MODERATOR", + displayName: "Chat Mod", + description: "Can timeout people", + permissions: ["MODERATE_MEMBERS"] + } +] as const satisfies ITag[]; + +export const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number | null, className?: string, useRemSizes?: boolean; }> & { Types: Record; }; + +// PermissionStore.computePermissions will not work here since it only gets permissions for the current user +export const computePermissions: (options: { + user?: { id: string; } | string | null; + context?: Guild | Channel | null; + overwrites?: Channel["permissionOverwrites"] | null; + checkElevated?: boolean /* = true */; + excludeGuildPermissions?: boolean /* = false */; +}) => bigint = findByCodeLazy(".getCurrentUser()", ".computeLurkerPermissionsAllowList()"); diff --git a/src/equicordplugins/moreUserTags/index.tsx b/src/equicordplugins/moreUserTags/index.tsx new file mode 100644 index 00000000..e85cd027 --- /dev/null +++ b/src/equicordplugins/moreUserTags/index.tsx @@ -0,0 +1,183 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./styles.css"; + +import { classNameFactory } from "@api/Styles"; +import { Devs } from "@utils/constants"; +import { getCurrentChannel, getIntlMessage } from "@utils/discord"; +import definePlugin from "@utils/types"; +import { ChannelStore, GuildStore, PermissionsBits, SelectedChannelStore, UserStore } from "@webpack/common"; +import { Channel, Message, User } from "discord-types/general"; + +import { computePermissions, Tag, tags } from "./consts"; +import { settings } from "./settings"; +import { TagSettings } from "./types"; + +const cl = classNameFactory("vc-mut-"); + +const genTagTypes = () => { + let i = 100; + const obj = {}; + + for (const { name } of tags) { + obj[name] = ++i; + obj[i] = name; + } + + return obj; +}; + +export default definePlugin({ + name: "MoreUserTags", + description: "Adds tags for webhooks and moderative roles (owner, admin, etc.)", + authors: [Devs.Cyn, Devs.TheSun, Devs.RyanCaoDev, Devs.LordElias, Devs.AutumnVN, Devs.hen], + dependencies: ["MemberListDecoratorsAPI", "NicknameIconsAPI", "MessageDecorationsAPI"], + settings, + patches: [ + // Make discord actually use our tags + { + find: ".STAFF_ONLY_DM:", + replacement: [{ + match: /(?<=type:(\i).{10,1000}.REMIX.{10,100})default:(\i)=/, + replace: "default:$2=$self.getTagText($self.localTags[$1]);", + }, { + match: /(?<=type:(\i).{10,1000}.REMIX.{10,100})\.BOT:(?=default:)/, + replace: "$&return null;", + predicate: () => settings.store.dontShowBotTag + }, + ], + } + ], + start() { + const tagSettings = settings.store.tagSettings || {} as TagSettings; + for (const tag of Object.values(tags)) { + tagSettings[tag.name] ??= { + showInChat: true, + showInNotChat: true, + text: tag.displayName + }; + } + + settings.store.tagSettings = tagSettings; + }, + localTags: genTagTypes(), + getChannelId() { + return SelectedChannelStore.getChannelId(); + }, + renderNicknameIcon(props) { + const tagId = this.getTag({ + user: UserStore.getUser(props.userId), + channel: ChannelStore.getChannel(this.getChannelId()), + channelId: this.getChannelId(), + isChat: false + }); + + return tagId && + ; + + }, + renderMessageDecoration(props) { + const tagId = this.getTag({ + message: props.message, + user: UserStore.getUser(props.message.author.id), + channelId: props.message.channel_id, + isChat: false + }); + + return tagId && + ; + }, + renderMemberListDecorator(props) { + const tagId = this.getTag({ + user: props.user, + channel: getCurrentChannel(), + channelId: this.getChannelId(), + isChat: false + }); + + return tagId && + ; + }, + + getTagText(tagName: string) { + if (!tagName) return getIntlMessage("APP_TAG"); + const tag = tags.find(({ name }) => tagName === name); + if (!tag) return tagName || getIntlMessage("APP_TAG"); + + return settings.store.tagSettings?.[tag.name]?.text || tag.displayName; + }, + + getTag({ + message, user, channelId, isChat, channel + }: { + message?: Message, + user?: User & { isClyde(): boolean; }, + channel?: Channel & { isForumPost(): boolean; isMediaPost(): boolean; }, + channelId?: string; + isChat?: boolean; + }): number | null { + const settings = this.settings.store; + + if (!user) return null; + if (isChat && user.id === "1") return null; + if (user.isClyde()) return null; + if (user.bot && settings.dontShowForBots) return null; + + channel ??= ChannelStore.getChannel(channelId!) as any; + if (!channel) return null; + + const perms = this.getPermissions(user, channel); + + for (const tag of tags) { + if (isChat && !settings.tagSettings[tag.name].showInChat) + continue; + if (!isChat && !settings.tagSettings[tag.name].showInNotChat) + continue; + + // If the owner tag is disabled, and the user is the owner of the guild, + // avoid adding other tags because the owner will always match the condition for them + if ( + (tag.name !== "OWNER" && + GuildStore.getGuild(channel?.guild_id)?.ownerId === + user.id && + isChat && + !settings.tagSettings.OWNER.showInChat) || + (!isChat && + !settings.tagSettings.OWNER.showInNotChat) + ) + continue; + + if ("permissions" in tag ? + tag.permissions.some(perm => perms.includes(perm)) : + tag.condition(message!, user, channel)) { + + return this.localTags[tag.name]; + } + } + + return null; + }, + getPermissions(user: User, channel: Channel): string[] { + const guild = GuildStore.getGuild(channel?.guild_id); + if (!guild) return []; + + const permissions = computePermissions({ user, context: guild, overwrites: channel.permissionOverwrites }); + return Object.entries(PermissionsBits) + .map(([perm, permInt]) => + permissions & permInt ? perm : "" + ) + .filter(Boolean); + }, +}); diff --git a/src/equicordplugins/moreUserTags/settings.tsx b/src/equicordplugins/moreUserTags/settings.tsx new file mode 100644 index 00000000..f48634e1 --- /dev/null +++ b/src/equicordplugins/moreUserTags/settings.tsx @@ -0,0 +1,81 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { Margins } from "@utils/margins"; +import { OptionType } from "@utils/types"; +import { Card, Flex, Forms, Switch, TextInput, Tooltip } from "@webpack/common"; + +import { Tag, tags } from "./consts"; +import { TagSettings } from "./types"; + +function SettingsComponent() { + const tagSettings = settings.store.tagSettings as TagSettings; + + return ( + + {tags.map(t => ( + + + + {({ onMouseEnter, onMouseLeave }) => ( +
+ {t.displayName} Tag +
+ )} +
+
+ + tagSettings[t.name].text = v} + className={Margins.bottom16} + /> + + tagSettings[t.name].showInChat = v} + hideBorder + > + Show in messages + + + tagSettings[t.name].showInNotChat = v} + hideBorder + > + Show in member list and profiles + +
+ ))} +
+ ); +} + +export const settings = definePluginSettings({ + dontShowForBots: { + description: "Don't show extra tags for bots (excluding webhooks)", + type: OptionType.BOOLEAN, + default: false + }, + dontShowBotTag: { + description: "Only show extra tags for bots / Hide [APP] text", + type: OptionType.BOOLEAN, + default: false, + restartNeeded: true + }, + tagSettings: { + type: OptionType.COMPONENT, + component: SettingsComponent, + description: "fill me" + } +}); diff --git a/src/equicordplugins/moreUserTags/styles.css b/src/equicordplugins/moreUserTags/styles.css new file mode 100644 index 00000000..41160325 --- /dev/null +++ b/src/equicordplugins/moreUserTags/styles.css @@ -0,0 +1,20 @@ +.vc-mut-message-tag { + /* Remove default margin from tags in messages */ + margin-top: unset !important; + + /* Align with Discord default tags in messages */ + position: relative; + bottom: 0.01em; +} + +.vc-mut-message-verified { + height: 1rem !important; +} + +span[class*="botTagCozy"][data-moreTags-darkFg="true"]>svg>path { + fill: #000; +} + +span[class*="botTagCozy"][data-moreTags-darkFg="false"]>svg>path { + fill: #fff; +} diff --git a/src/equicordplugins/moreUserTags/types.ts b/src/equicordplugins/moreUserTags/types.ts new file mode 100644 index 00000000..40d8ac7d --- /dev/null +++ b/src/equicordplugins/moreUserTags/types.ts @@ -0,0 +1,32 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import type { Permissions } from "@webpack/types"; +import type { Channel, Message, User } from "discord-types/general"; + +import { tags } from "./consts"; + +export type ITag = { + // name used for identifying, must be alphanumeric + underscores + name: string; + // name shown on the tag itself, can be anything probably; automatically uppercase'd + displayName: string; + description: string; +} & ({ + permissions: Permissions[]; +} | { + condition?(message: Message | null, user: User, channel: Channel): boolean; +}); + +export interface TagSetting { + text: string; + showInChat: boolean; + showInNotChat: boolean; +} + +export type TagSettings = { + [k in typeof tags[number]["name"]]: TagSetting; +}; diff --git a/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx b/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx index 17e5c4ad..0f6adb75 100644 --- a/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx +++ b/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx @@ -125,7 +125,14 @@ export default ErrorBoundary.wrap(function NotificationComponent({
- {renderBody ? richBody ??

{body}

: null} + {renderBody ? ( + richBody ?? ( +

+ {body.length > 500 ? body.slice(0, 500) + "..." : body} +

+ ) + ) : null} + {PluginSettings.store.renderImages && image && ToastNotification Image} {footer &&

{`${attachments} attachment${attachments > 1 ? "s" : ""} ${attachments > 1 ? "were" : "was"} sent.`}

}
diff --git a/src/plugins/_api/nicknameIcons.ts b/src/plugins/_api/nicknameIcons.ts new file mode 100644 index 00000000..be3763cc --- /dev/null +++ b/src/plugins/_api/nicknameIcons.ts @@ -0,0 +1,23 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "NicknameIconsAPI", + description: "API to add icons to the nickname, in profiles", + authors: [Devs.Nuckyz], + patches: [ + { + find: "#{intl::USER_PROFILE_LOAD_ERROR}", + replacement: { + match: /(\.fetchError.+?\?)null/, + replace: (_, rest) => `${rest}Vencord.Api.NicknameIcons._renderIcons(arguments[0])` + } + } + ] +}); diff --git a/src/plugins/index.ts b/src/plugins/index.ts index e5594e99..c1ca6023 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -25,6 +25,7 @@ import { addMessageAccessory, removeMessageAccessory } from "@api/MessageAccesso import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations"; import { addMessageClickListener, addMessagePreEditListener, addMessagePreSendListener, removeMessageClickListener, removeMessagePreEditListener, removeMessagePreSendListener } from "@api/MessageEvents"; import { addMessagePopoverButton, removeMessagePopoverButton } from "@api/MessagePopover"; +import { addNicknameIcon, removeNicknameIcon } from "@api/NicknameIcons"; import { Settings, SettingsStore } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import { Logger } from "@utils/Logger"; @@ -96,7 +97,7 @@ function isReporterTestable(p: Plugin, part: ReporterTestable) { const pluginKeysToBind: Array = [ "onBeforeMessageEdit", "onBeforeMessageSend", "onMessageClick", - "renderChatBarButton", "renderMemberListDecorator", "renderMessageAccessory", "renderMessageDecoration", "renderMessagePopoverButton" + "renderChatBarButton", "renderMemberListDecorator", "renderNicknameIcon", "renderMessageAccessory", "renderMessageDecoration", "renderMessagePopoverButton" ]; const neededApiPlugins = new Set(); @@ -128,6 +129,7 @@ for (const p of pluginsValues) if (isPluginEnabled(p.name)) { if (p.onBeforeMessageEdit || p.onBeforeMessageSend || p.onMessageClick) neededApiPlugins.add("MessageEventsAPI"); if (p.renderChatBarButton) neededApiPlugins.add("ChatInputButtonAPI"); if (p.renderMemberListDecorator) neededApiPlugins.add("MemberListDecoratorsAPI"); + if (p.renderNicknameIcon) neededApiPlugins.add("NicknameIconsAPI"); if (p.renderMessageAccessory) neededApiPlugins.add("MessageAccessoriesAPI"); if (p.renderMessageDecoration) neededApiPlugins.add("MessageDecorationsAPI"); if (p.renderMessagePopoverButton) neededApiPlugins.add("MessagePopoverAPI"); @@ -261,7 +263,7 @@ export const startPlugin = traceFunction("startPlugin", function startPlugin(p: const { name, commands, contextMenus, managedStyle, userProfileBadge, onBeforeMessageEdit, onBeforeMessageSend, onMessageClick, - renderChatBarButton, renderMemberListDecorator, renderMessageAccessory, renderMessageDecoration, renderMessagePopoverButton + renderChatBarButton, renderMemberListDecorator, renderNicknameIcon, renderMessageAccessory, renderMessageDecoration, renderMessagePopoverButton } = p; if (p.start) { @@ -313,6 +315,7 @@ export const startPlugin = traceFunction("startPlugin", function startPlugin(p: if (renderChatBarButton) addChatBarButton(name, renderChatBarButton); if (renderMemberListDecorator) addMemberListDecorator(name, renderMemberListDecorator); + if (renderNicknameIcon) addNicknameIcon(name, renderNicknameIcon); if (renderMessageDecoration) addMessageDecoration(name, renderMessageDecoration); if (renderMessageAccessory) addMessageAccessory(name, renderMessageAccessory); if (renderMessagePopoverButton) addMessagePopoverButton(name, renderMessagePopoverButton); @@ -324,7 +327,7 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu const { name, commands, contextMenus, managedStyle, userProfileBadge, onBeforeMessageEdit, onBeforeMessageSend, onMessageClick, - renderChatBarButton, renderMemberListDecorator, renderMessageAccessory, renderMessageDecoration, renderMessagePopoverButton + renderChatBarButton, renderMemberListDecorator, renderNicknameIcon, renderMessageAccessory, renderMessageDecoration, renderMessagePopoverButton } = p; if (p.stop) { @@ -374,6 +377,7 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu if (renderChatBarButton) removeChatBarButton(name); if (renderMemberListDecorator) removeMemberListDecorator(name); + if (renderNicknameIcon) removeNicknameIcon(name); if (renderMessageDecoration) removeMessageDecoration(name); if (renderMessageAccessory) removeMessageAccessory(name); if (renderMessagePopoverButton) removeMessagePopoverButton(name); diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index f6550e65..0188959a 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -18,15 +18,15 @@ import "./style.css"; -import { addProfileBadge, BadgePosition, BadgeUserArgs, ProfileBadge, removeProfileBadge } from "@api/Badges"; import { addMemberListDecorator, removeMemberListDecorator } from "@api/MemberListDecorators"; import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations"; -import { Settings } from "@api/Settings"; -import ErrorBoundary from "@components/ErrorBoundary"; +import { addNicknameIcon, removeNicknameIcon } from "@api/NicknameIcons"; +import { definePluginSettings, migratePluginSetting } from "@api/Settings"; import { Devs } from "@utils/constants"; +import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; import { filters, findStoreLazy, mapMangledModuleLazy } from "@webpack"; -import { PresenceStore, Tooltip, UserStore } from "@webpack/common"; +import { PresenceStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; import { User } from "discord-types/general"; export interface Session { @@ -44,10 +44,26 @@ const SessionsStore = findStoreLazy("SessionsStore") as { getSessions(): Record; }; -function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) { - return ({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) => ( +const { useStatusFillColor } = mapMangledModuleLazy(".concat(.5625*", { + useStatusFillColor: filters.byCode(".hex") +}); + +interface IconFactoryOpts { + viewBox?: string; + width?: number; + height?: number; +} + +interface IconProps { + color: string; + tooltip: string; + small?: boolean; +} + +function Icon(path: string, opts?: IconFactoryOpts) { + return ({ color, tooltip, small }: IconProps) => ( - {(tooltipProps: any) => ( + {tooltipProps => ( { +const PlatformIcon = ({ platform, status, small }: PlatformIconProps) => { const tooltip = platform === "embedded" ? "Console" : platform[0].toUpperCase() + platform.slice(1); let Icon = Icons[platform] ?? Icons.desktop; - const { ConsoleIcon } = Settings.plugins.PlatformIndicators; + const { ConsoleIcon } = settings.store; if (platform === "embedded" && ConsoleIcon === "vencord") { Icon = Icons.vencord; } @@ -92,159 +112,166 @@ const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: return ; }; -function ensureOwnStatus(user: User) { - if (user.id === UserStore.getCurrentUser().id) { - const sessions = SessionsStore.getSessions(); - if (typeof sessions !== "object") return null; - const sortedSessions = Object.values(sessions).sort(({ status: a }, { status: b }) => { - if (a === b) return 0; - if (a === "online") return 1; - if (b === "online") return -1; - if (a === "idle") return 1; - if (b === "idle") return -1; - return 0; - }); - - const ownStatus = Object.values(sortedSessions).reduce((acc, curr) => { - if (curr.clientInfo.client !== "unknown") - acc[curr.clientInfo.client] = curr.status; - return acc; - }, {}); - - const { clientStatuses } = PresenceStore.getState(); - clientStatuses[UserStore.getCurrentUser().id] = ownStatus; +function useEnsureOwnStatus(user: User) { + if (user.id !== UserStore.getCurrentUser()?.id) { + return; } + + const sessions = useStateFromStores([SessionsStore], () => SessionsStore.getSessions()); + if (typeof sessions !== "object") return null; + const sortedSessions = Object.values(sessions).sort(({ status: a }, { status: b }) => { + if (a === b) return 0; + if (a === "online") return 1; + if (b === "online") return -1; + if (a === "idle") return 1; + if (b === "idle") return -1; + return 0; + }); + + const ownStatus = Object.values(sortedSessions).reduce((acc, curr) => { + if (curr.clientInfo.client !== "unknown") + acc[curr.clientInfo.client] = curr.status; + return acc; + }, {}); + + const { clientStatuses } = PresenceStore.getState(); + clientStatuses[UserStore.getCurrentUser().id] = ownStatus; } -function getBadges({ userId }: BadgeUserArgs): ProfileBadge[] { - const user = UserStore.getUser(userId); - - if (!user || (user.bot && !Settings.plugins.PlatformIndicators.showBots)) return []; - - ensureOwnStatus(user); - - const status = PresenceStore.getState()?.clientStatuses?.[user.id] as Record; - if (!status) return []; - - return Object.entries(status).map(([platform, status]) => ({ - component: () => ( - - - - ), - key: `vc-platform-indicator-${platform}` - })); +interface PlatformIndicatorProps { + user: User; + isProfile?: boolean; + isMessage?: boolean; + isMemberList?: boolean; } -const PlatformIndicator = ({ user, small = false }: { user: User; small?: boolean; }) => { - if (!user || (user.bot && !Settings.plugins.PlatformIndicators.showBots)) return null; +const PlatformIndicator = ({ user, isProfile, isMessage, isMemberList }: PlatformIndicatorProps) => { + if (user == null || user.bot) return null; + useEnsureOwnStatus(user); - ensureOwnStatus(user); + const status: Record | undefined = useStateFromStores([PresenceStore], () => PresenceStore.getState()?.clientStatuses?.[user.id]); + if (status == null) { + return null; + } - const status = PresenceStore.getState()?.clientStatuses?.[user.id] as Record; - if (!status) return null; - - const icons = Object.entries(status).map(([platform, status]) => ( + const icons = Array.from(Object.entries(status), ([platform, status]) => ( )); - if (!icons.length) return null; + if (!icons.length) { + return null; + } return ( - {icons} - +
); }; -const badge: ProfileBadge = { - getBadges, - position: BadgePosition.START, -}; +function toggleMemberListDecorators(enabled: boolean) { + if (enabled) { + addMemberListDecorator("PlatformIndicators", props => ); + } else { + removeMemberListDecorator("PlatformIndicators"); + } +} -const indicatorLocations = { +function toggleNicknameIcons(enabled: boolean) { + if (enabled) { + addNicknameIcon("PlatformIndicators", props => , 1); + } else { + removeNicknameIcon("PlatformIndicators"); + } +} + +function toggleMessageDecorators(enabled: boolean) { + if (enabled) { + addMessageDecoration("PlatformIndicators", props => ); + } else { + removeMessageDecoration("PlatformIndicators"); + } +} + +migratePluginSetting("PlatformIndicators", "badges", "profiles"); +const settings = definePluginSettings({ list: { - description: "In the member list", - onEnable: () => addMemberListDecorator("platform-indicator", props => - - - - ), - onDisable: () => removeMemberListDecorator("platform-indicator") + type: OptionType.BOOLEAN, + description: "Show indicators in the member list", + default: true, + onChange: toggleMemberListDecorators }, - badges: { - description: "In user profiles, as badges", - onEnable: () => addProfileBadge(badge), - onDisable: () => removeProfileBadge(badge) + profiles: { + type: OptionType.BOOLEAN, + description: "Show indicators in user profiles", + default: true, + onChange: toggleNicknameIcons }, messages: { - description: "Inside messages", - onEnable: () => addMessageDecoration("platform-indicator", props => - - - - ), - onDisable: () => removeMessageDecoration("platform-indicator") + type: OptionType.BOOLEAN, + description: "Show indicators inside messages", + default: true, + onChange: toggleMessageDecorators + }, + colorMobileIndicator: { + type: OptionType.BOOLEAN, + description: "Whether to make the mobile indicator match the color of the user status.", + default: true, + restartNeeded: true + }, + ConsoleIcon: { + type: OptionType.SELECT, + description: "What console icon to use", + restartNeeded: true, + options: [ + { + label: "Equicord", + value: "equicord", + default: true + }, + { + label: "Suncord", + value: "suncord", + }, + { + label: "Vencord", + value: "vencord", + }, + ], } -}; - -function addAllIndicators() { - const settings = Settings.plugins.PlatformIndicators; - const { displayMode } = settings; - - // transfer settings from the old ones, which had a select menu instead of booleans - if (displayMode) { - if (displayMode !== "both") settings[displayMode] = true; - else { - settings.list = true; - settings.badges = true; - } - settings.messages = true; - delete settings.displayMode; - } - - Object.entries(indicatorLocations).forEach(([key, value]) => { - if (settings[key]) value.onEnable(); - }); -} - -function deleteAllIndicators() { - Object.entries(indicatorLocations).forEach(([_, value]) => { - value.onDisable(); - }); -} +}); export default definePlugin({ name: "PlatformIndicators", description: "Adds platform indicators (Desktop, Mobile, Web...) to users", authors: [Devs.kemo, Devs.TheSun, Devs.Nuckyz, Devs.Ven], - dependencies: ["MessageDecorationsAPI", "MemberListDecoratorsAPI"], + dependencies: ["MemberListDecoratorsAPI", "NicknameIconsAPI", "MessageDecorationsAPI"], + settings, start() { - addAllIndicators(); + if (settings.store.list) toggleMemberListDecorators(true); + if (settings.store.profiles) toggleNicknameIcons(true); + if (settings.store.messages) toggleMessageDecorators(true); }, stop() { - deleteAllIndicators(); + if (settings.store.list) toggleMemberListDecorators(false); + if (settings.store.profiles) toggleNicknameIcons; + if (settings.store.messages) toggleMessageDecorators(false); }, patches: [ { find: ".Masks.STATUS_ONLINE_MOBILE", - predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, + predicate: () => settings.store.colorMobileIndicator, replacement: [ { // Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status @@ -260,7 +287,7 @@ export default definePlugin({ }, { find: ".AVATAR_STATUS_MOBILE_16;", - predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, + predicate: () => settings.store.colorMobileIndicator, replacement: [ { // Return the AVATAR_STATUS_MOBILE size mask if the user is on mobile, no matter the status @@ -281,58 +308,12 @@ export default definePlugin({ }, { find: "}isMobileOnline(", - predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, + predicate: () => settings.store.colorMobileIndicator, replacement: { // Make isMobileOnline return true no matter what is the user status match: /(?<=\i\[\i\.\i\.MOBILE\])===\i\.\i\.ONLINE/, replace: "!= null" } } - ], - - options: { - ...Object.fromEntries( - Object.entries(indicatorLocations).map(([key, value]) => { - return [key, { - type: OptionType.BOOLEAN, - description: `Show indicators ${value.description.toLowerCase()}`, - // onChange doesn't give any way to know which setting was changed, so restart required - restartNeeded: true, - default: true - }]; - }) - ), - colorMobileIndicator: { - type: OptionType.BOOLEAN, - description: "Whether to make the mobile indicator match the color of the user status.", - default: true, - restartNeeded: true - }, - showBots: { - type: OptionType.BOOLEAN, - description: "Whether to show platform indicators on bots", - default: false, - restartNeeded: false - }, - ConsoleIcon: { - type: OptionType.SELECT, - description: "What console icon to use", - restartNeeded: true, - options: [ - { - label: "Equicord", - value: "equicord", - default: true - }, - { - label: "Suncord", - value: "suncord", - }, - { - label: "Vencord", - value: "vencord", - }, - ], - }, - } + ] }); diff --git a/src/plugins/platformIndicators/style.css b/src/plugins/platformIndicators/style.css index 38ea5ef4..a5566fdd 100644 --- a/src/plugins/platformIndicators/style.css +++ b/src/plugins/platformIndicators/style.css @@ -2,6 +2,20 @@ display: inline-flex; justify-content: center; align-items: center; - vertical-align: top; - position: relative; + gap: 2px; +} + +.vc-platform-indicator-profile { + background: rgb(var(--bg-overlay-color) / var(--bg-overlay-opacity-6)); + border: 1px solid var(--border-faint); + border-radius: var(--radius-xs); + border-color: var(--profile-body-border-color); + margin: 0 1px; + padding: 0 1px; +} + +.vc-platform-indicator-message { + position: relative; + vertical-align: top; + top: 2px; } diff --git a/src/plugins/userVoiceShow/components.tsx b/src/plugins/userVoiceShow/components.tsx index 675588ed..136febd6 100644 --- a/src/plugins/userVoiceShow/components.tsx +++ b/src/plugins/userVoiceShow/components.tsx @@ -111,32 +111,32 @@ function VoiceChannelTooltip({ channel, isLocked }: VoiceChannelTooltipProps) { {guild.name}
)} -
+
{channelIcon} {channelName} -
- + {users.length < 10 ? `0${users.length}` : `${users.length}`} - {channel.userLimit < 10 ? `0${channel.userLimit}` : `${channel.userLimit}`} @@ -156,8 +156,10 @@ function VoiceChannelTooltip({ channel, isLocked }: VoiceChannelTooltipProps) { ); } -export interface VoiceChannelIndicatorProps { +interface VoiceChannelIndicatorProps { userId: string; + isMessageIndicator?: boolean; + isProfile?: boolean; isActionButton?: boolean; shouldHighlight?: boolean; } diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx index 67e5e022..5f1a05e5 100644 --- a/src/plugins/userVoiceShow/index.tsx +++ b/src/plugins/userVoiceShow/index.tsx @@ -20,6 +20,7 @@ import "./style.css"; import { addMemberListDecorator, removeMemberListDecorator } from "@api/MemberListDecorators"; import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations"; +import { addNicknameIcon, removeNicknameIcon } from "@api/NicknameIcons"; import { definePluginSettings } from "@api/Settings"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; @@ -51,38 +52,10 @@ export default definePlugin({ name: "UserVoiceShow", description: "Shows an indicator when a user is in a Voice Channel", authors: [Devs.Nuckyz, Devs.LordElias, EquicordDevs.omaw], - dependencies: ["MemberListDecoratorsAPI", "MessageDecorationsAPI"], + dependencies: ["NicknameIconsAPI", "MemberListDecoratorsAPI", "MessageDecorationsAPI"], settings, patches: [ - // User Popout, Full Size Profile, Direct Messages Side Profile - { - find: "#{intl::USER_PROFILE_LOAD_ERROR}", - replacement: { - match: /(\.fetchError.+?\?)null/, - replace: (_, rest) => `${rest}$self.VoiceChannelIndicator({userId:arguments[0]?.userId})` - }, - predicate: () => settings.store.showInUserProfileModal - }, - // To use without the MemberList decorator API - /* // Guild Members List - { - find: ".lostPermission)", - replacement: { - match: /\.lostPermission\).+?(?=avatar:)/, - replace: "$&children:[$self.VoiceChannelIndicator({userId:arguments[0]?.user?.id})]," - }, - predicate: () => settings.store.showVoiceChannelIndicator - }, - // Direct Messages List - { - find: "PrivateChannel.renderAvatar", - replacement: { - match: /#{intl::CLOSE_DM}.+?}\)(?=])/, - replace: "$&,$self.VoiceChannelIndicator({userId:arguments[0]?.user?.id})" - }, - predicate: () => settings.store.showVoiceChannelIndicator - }, */ // Friends List { find: "null!=this.peopleListItemRef.current", @@ -95,15 +68,19 @@ export default definePlugin({ ], start() { + if (settings.store.showInUserProfileModal) { + addNicknameIcon("UserVoiceShow", ({ userId }) => ); + } if (settings.store.showInMemberList) { addMemberListDecorator("UserVoiceShow", ({ user }) => user == null ? null : ); } if (settings.store.showInMessages) { - addMessageDecoration("UserVoiceShow", ({ message }) => message?.author == null ? null : ); + addMessageDecoration("UserVoiceShow", ({ message }) => message?.author == null ? null : ); } }, stop() { + removeNicknameIcon("UserVoiceShow"); removeMemberListDecorator("UserVoiceShow"); removeMessageDecoration("UserVoiceShow"); }, diff --git a/src/utils/constants.ts b/src/utils/constants.ts index b798c88e..5a230a6a 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -606,6 +606,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "samsam", id: 836452332387565589n, }, + hen: { + id: 279266228151779329n, + name: "Hen" + }, } satisfies Record); export const EquicordDevs = Object.freeze({ diff --git a/src/utils/types.ts b/src/utils/types.ts index 270a7a09..b2435c6f 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -25,6 +25,7 @@ import { MessageAccessoryFactory } from "@api/MessageAccessories"; import { MessageDecorationFactory } from "@api/MessageDecorations"; import { MessageClickListener, MessageEditListener, MessageSendListener } from "@api/MessageEvents"; import { MessagePopoverButtonFactory } from "@api/MessagePopover"; +import { NicknameIconFactory } from "@api/NicknameIcons"; import { FluxEvents } from "@webpack/types"; import { ReactNode } from "react"; import { Promisable } from "type-fest"; @@ -187,6 +188,7 @@ export interface PluginDef { renderMessageDecoration?: MessageDecorationFactory; renderMemberListDecorator?: MemberListDecoratorFactory; + renderNicknameIcon?: NicknameIconFactory; renderChatBarButton?: ChatBarButtonFactory; } From b140cc0d39081cb5291e5b4547383119f79cf1c6 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 2 May 2025 21:02:28 -0400 Subject: [PATCH 090/198] "Fix Lint"??? Nah --- src/equicordplugins/moreUserTags/index.tsx | 4 ++-- src/utils/constants.ts | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/equicordplugins/moreUserTags/index.tsx b/src/equicordplugins/moreUserTags/index.tsx index e85cd027..7b3b27ae 100644 --- a/src/equicordplugins/moreUserTags/index.tsx +++ b/src/equicordplugins/moreUserTags/index.tsx @@ -7,7 +7,7 @@ import "./styles.css"; import { classNameFactory } from "@api/Styles"; -import { Devs } from "@utils/constants"; +import { Devs, EquicordDevs } from "@utils/constants"; import { getCurrentChannel, getIntlMessage } from "@utils/discord"; import definePlugin from "@utils/types"; import { ChannelStore, GuildStore, PermissionsBits, SelectedChannelStore, UserStore } from "@webpack/common"; @@ -34,7 +34,7 @@ const genTagTypes = () => { export default definePlugin({ name: "MoreUserTags", description: "Adds tags for webhooks and moderative roles (owner, admin, etc.)", - authors: [Devs.Cyn, Devs.TheSun, Devs.RyanCaoDev, Devs.LordElias, Devs.AutumnVN, Devs.hen], + authors: [Devs.Cyn, Devs.TheSun, Devs.RyanCaoDev, Devs.LordElias, Devs.AutumnVN, EquicordDevs.Hen], dependencies: ["MemberListDecoratorsAPI", "NicknameIconsAPI", "MessageDecorationsAPI"], settings, patches: [ diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 5a230a6a..b798c88e 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -606,10 +606,6 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "samsam", id: 836452332387565589n, }, - hen: { - id: 279266228151779329n, - name: "Hen" - }, } satisfies Record); export const EquicordDevs = Object.freeze({ From e6ab96a3eed9b4d8e35dccf4d298e1a763e29c22 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 2 May 2025 21:07:44 -0400 Subject: [PATCH 091/198] Fix Formatting --- src/equicordplugins/moreUserTags/index.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/equicordplugins/moreUserTags/index.tsx b/src/equicordplugins/moreUserTags/index.tsx index 7b3b27ae..b45dac04 100644 --- a/src/equicordplugins/moreUserTags/index.tsx +++ b/src/equicordplugins/moreUserTags/index.tsx @@ -41,14 +41,16 @@ export default definePlugin({ // Make discord actually use our tags { find: ".STAFF_ONLY_DM:", - replacement: [{ - match: /(?<=type:(\i).{10,1000}.REMIX.{10,100})default:(\i)=/, - replace: "default:$2=$self.getTagText($self.localTags[$1]);", - }, { - match: /(?<=type:(\i).{10,1000}.REMIX.{10,100})\.BOT:(?=default:)/, - replace: "$&return null;", - predicate: () => settings.store.dontShowBotTag - }, + replacement: [ + { + match: /(?<=type:(\i).{10,1000}.REMIX.{10,100})default:(\i)=/, + replace: "default:$2=$self.getTagText($self.localTags[$1]);", + }, + { + match: /(?<=type:(\i).{10,1000}.REMIX.{10,100})\.BOT:(?=default:)/, + replace: "$&return null;", + predicate: () => settings.store.dontShowBotTag + }, ], } ], From bebf3dd0681a5a8c8a81f7e501f06a0738b763af Mon Sep 17 00:00:00 2001 From: hexa <67880600+hexa0@users.noreply.github.com> Date: Fri, 2 May 2025 21:28:53 -0400 Subject: [PATCH 092/198] ImageZoom: Fix for animated WebP images (#3407) --- src/plugins/imageZoom/components/Magnifier.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/imageZoom/components/Magnifier.tsx b/src/plugins/imageZoom/components/Magnifier.tsx index 009165ff..2acd0259 100644 --- a/src/plugins/imageZoom/components/Magnifier.tsx +++ b/src/plugins/imageZoom/components/Magnifier.tsx @@ -203,7 +203,7 @@ export const Magnifier = ErrorBoundary.wrap(({ instance, size: i }} width={`${box.width * zoom.current}px`} height={`${box.height * zoom.current}px`} - src={instance.props.src} + src={instance.props.src + "?animated=true"} alt="" /> )} From 59974a162eca499f1562b7f6ba43c7644b689159 Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Fri, 2 May 2025 21:30:10 -0400 Subject: [PATCH 093/198] PlainFolderIcon: Fix plugin not working (#3409) --- src/plugins/betterFolders/index.tsx | 11 ++++++--- src/plugins/mutualGroupDMs/index.tsx | 21 ++++++++-------- src/plugins/plainFolderIcon/index.ts | 35 +++++++++++++-------------- src/plugins/plainFolderIcon/style.css | 10 ++++++++ 4 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 src/plugins/plainFolderIcon/style.css diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 2b1defd8..b5049883 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -61,7 +61,7 @@ function filterTreeWithTargetNode(children: any, predicate: (node: any) => boole return true; } - return filterTreeWithTargetNode(children.props.children, predicate); + return filterTreeWithTargetNode(children.props?.children, predicate); } @@ -150,7 +150,7 @@ export default definePlugin({ }, // Export the isBetterFolders and betterFoldersExpandedIds variable to the Guild List component { - match: /0,\i\.jsxs?[^0}]{0,100}guildDiscoveryButton:\i,/g, + match: /,{guildDiscoveryButton:\i,/g, replace: "$&isBetterFolders:arguments[0]?.isBetterFolders,betterFoldersExpandedIds:arguments[0]?.betterFoldersExpandedIds," }, // Export the isBetterFolders variable to the folders component @@ -349,7 +349,12 @@ export default definePlugin({ return true; } - return filterTreeWithTargetNode(child, child => child?.props?.renderTreeNode != null); + try { + return filterTreeWithTargetNode(child, child => child?.props?.renderTreeNode != null); + } catch (e) { + console.error(e); + return true; + } }; }, diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 42f4f2e3..f06cac0c 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -59,22 +59,21 @@ function renderClickableGDMs(mutualDms: Channel[], onClose: () => void) { return mutualDms.map(c => ( { onClose(); SelectedChannelActionCreators.selectPrivateChannel(c.id); }} > -
- - -
-
{getGroupDMName(c)}
-
{c.recipients.length + 1} Members
-
+ + +
+
{getGroupDMName(c)}
+
{c.recipients.length + 1} Members
)); diff --git a/src/plugins/plainFolderIcon/index.ts b/src/plugins/plainFolderIcon/index.ts index bb6876b5..8eb87896 100644 --- a/src/plugins/plainFolderIcon/index.ts +++ b/src/plugins/plainFolderIcon/index.ts @@ -16,28 +16,27 @@ * along with this program. If not, see . */ +import "./style.css"; + import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; export default definePlugin({ name: "PlainFolderIcon", - description: "Doesn't show the small guild icons in folders", + description: "Dont show the small guild icons in folders", authors: [Devs.botato], - patches: [{ - find: ".expandedFolderIconWrapper", - replacement: [ - // there are two elements, the first one is the plain folder icon - // the second is the four guild preview icons - // always show this one (the plain icons) - { - match: /\(\i\|\|\i\)&&(\(.{0,40}\(\i\.animated)/, - replace: "$1", - }, - // and never show this one (the guild preview icons) - { - match: /\(\i\|\|!\i\)&&(\(.{0,40}\(\i\.animated)/, - replace: "false&&$1", - } - ] - }] + + patches: [ + { + find: ".folderPreviewGuildIconError", + replacement: [ + { + // Discord always renders both plain and guild icons folders and uses a css transtion to switch between them + match: /(?<=.folderButtonContent]:(!\i))/, + replace: (_, hasFolderButtonContentClass) => `,"vc-plainFolderIcon-plain":${hasFolderButtonContentClass}` + } + + ] + } + ] }); diff --git a/src/plugins/plainFolderIcon/style.css b/src/plugins/plainFolderIcon/style.css new file mode 100644 index 00000000..70a49078 --- /dev/null +++ b/src/plugins/plainFolderIcon/style.css @@ -0,0 +1,10 @@ +.vc-plainFolderIcon-plain { + /* Without this, they are a bit laggier */ + transition: none !important; + + /* Don't show the mini guild icons */ + transform: translateZ(0); + + /* The new icons are fully transparent. Add a sane default to match the old behavior */ + background-color: color-mix(in oklab, var(--custom-folder-color, var(--bg-brand)) 30%, var(--background-surface-higher) 70%); +} From 767599b89c48af24c7a6a315748c6f3775033942 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 2 May 2025 21:36:30 -0400 Subject: [PATCH 094/198] Fix Tags In Settings MoreUserTags --- src/equicordplugins/moreUserTags/settings.tsx | 3 ++- src/equicordplugins/splitLargeMessages/index.ts | 8 +------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/equicordplugins/moreUserTags/settings.tsx b/src/equicordplugins/moreUserTags/settings.tsx index f48634e1..df006ef8 100644 --- a/src/equicordplugins/moreUserTags/settings.tsx +++ b/src/equicordplugins/moreUserTags/settings.tsx @@ -14,6 +14,7 @@ import { TagSettings } from "./types"; function SettingsComponent() { const tagSettings = settings.store.tagSettings as TagSettings; + const { localTags } = Vencord.Plugins.plugins.MoreUserTags as any; return ( @@ -26,7 +27,7 @@ function SettingsComponent() { onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} > - {t.displayName} Tag + {t.displayName} Tag
)} diff --git a/src/equicordplugins/splitLargeMessages/index.ts b/src/equicordplugins/splitLargeMessages/index.ts index 88b4e426..cb19fe70 100644 --- a/src/equicordplugins/splitLargeMessages/index.ts +++ b/src/equicordplugins/splitLargeMessages/index.ts @@ -85,31 +85,26 @@ const settings = definePluginSettings({ maxLength = autoMaxLength(); }, }, - disableFileConversion: { type: OptionType.BOOLEAN, description: "If true, disables file conversion for large messages.", default: true, }, - sendDelay: { type: OptionType.SLIDER, description: "Delay between each chunk in seconds.", default: 1, markers: [1, 2, 3, 5, 10], }, - hardSplit: { type: OptionType.BOOLEAN, description: "If true, splits on the last character instead of the last space/newline.", default: false, }, - splitInSlowmode: { type: OptionType.BOOLEAN, description: "Should messages be split if the channel has slowmode enabled?", }, - slowmodeMax: { type: OptionType.NUMBER, description: "Maximum slowmode time if splitting in slowmode.", @@ -127,8 +122,7 @@ export default definePlugin({ settings, start() { - if (settings.store.maxLength === 0) - maxLength = autoMaxLength(); + if (settings.store.maxLength === 0) maxLength = autoMaxLength(); addMessagePreSendListener(listener); }, From ac80f3874d3ababe001510a93b45ce96b8074f0e Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 2 May 2025 21:44:15 -0400 Subject: [PATCH 095/198] Remove Extra Style CSS --- src/plugins/plainFolderIcon/styles.css | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 src/plugins/plainFolderIcon/styles.css diff --git a/src/plugins/plainFolderIcon/styles.css b/src/plugins/plainFolderIcon/styles.css deleted file mode 100644 index f832b404..00000000 --- a/src/plugins/plainFolderIcon/styles.css +++ /dev/null @@ -1,12 +0,0 @@ -.vc-plainFolderIcon-plain { - /* Without this, this are a bit laggier */ - transition: none !important; - - /* Don't show the mini guild icons */ - transform: none; -} - -.vc-plainFolderIcon-plain svg { - width: 35px; - height: 35px; -} From 3b53ad0c919b0b95e415640ddb83a4657aaedb0a Mon Sep 17 00:00:00 2001 From: Mufaro <81554673+mufaroxyz@users.noreply.github.com> Date: Sat, 3 May 2025 03:46:34 +0200 Subject: [PATCH 096/198] Experiments: Support new experiment link embeds & fix toolbar patch (#3372) --- src/plugins/betterFolders/index.tsx | 1 - src/plugins/experiments/index.tsx | 28 +++++++++++++------ .../imageZoom/components/Magnifier.tsx | 14 ++++++++-- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index b5049883..fc5b4436 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -64,7 +64,6 @@ function filterTreeWithTargetNode(children: any, predicate: (node: any) => boole return filterTreeWithTargetNode(children.props?.children, predicate); } - let childIsTargetChild = false; for (let i = 0; i < children.length; i++) { const shouldKeep = filterTreeWithTargetNode(children[i], predicate); diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 82e20f73..49ae1ab7 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -31,6 +31,10 @@ import hideBugReport from "./hideBugReport.css?managed"; const KbdStyles = findByPropsLazy("key", "combo"); const BugReporterExperiment = findLazy(m => m?.definition?.id === "2024-09_bug_reporter"); +const isMacOS = navigator.platform.includes("Mac"); +const modKey = isMacOS ? "cmd" : "ctrl"; +const altKey = isMacOS ? "opt" : "alt"; + const settings = definePluginSettings({ toolbarDevMenu: { type: OptionType.BOOLEAN, @@ -48,7 +52,7 @@ export default definePlugin({ Devs.Ven, Devs.Nickyux, Devs.BanTheNons, - Devs.Nuckyz + Devs.Nuckyz, ], settings, @@ -75,9 +79,9 @@ export default definePlugin({ replace: "$&$self.WarningCard()," } }, - // change top right chat toolbar button from the help one to the dev one + // Change top right chat toolbar button from the help one to the dev one { - find: "toolbar:function", + find: ".CONTEXTLESS,isActivityPanelMode:", replacement: { match: /hasBugReporterAccess:(\i)/, replace: "_hasBugReporterAccess:$1=true" @@ -85,7 +89,7 @@ export default definePlugin({ predicate: () => settings.store.toolbarDevMenu }, - // makes the Favourites Server experiment allow favouriting DMs and threads + // Make the Favourites Server experiment allow favouriting DMs and threads { find: "useCanFavoriteChannel", replacement: { @@ -93,23 +97,29 @@ export default definePlugin({ replace: "false", } }, - // enable option to always record clips even if you are not streaming + // Enable option to always record clips even if you are not streaming { find: "isDecoupledGameClippingEnabled(){", replacement: { match: /\i\.isStaff\(\)/, replace: "true" } - } + }, + + // Enable experiment embed on sent experiment links + { + find: "dev://experiment/", + replacement: { + match: /\i\.isStaff\(\)/, + replace: "true" + } + }, ], start: () => !BugReporterExperiment.getCurrentConfig().hasBugReporterAccess && enableStyle(hideBugReport), stop: () => disableStyle(hideBugReport), settingsAboutComponent: () => { - const isMacOS = navigator.platform.includes("Mac"); - const modKey = isMacOS ? "cmd" : "ctrl"; - const altKey = isMacOS ? "opt" : "alt"; return ( More Information diff --git a/src/plugins/imageZoom/components/Magnifier.tsx b/src/plugins/imageZoom/components/Magnifier.tsx index 2acd0259..d68f916e 100644 --- a/src/plugins/imageZoom/components/Magnifier.tsx +++ b/src/plugins/imageZoom/components/Magnifier.tsx @@ -18,7 +18,7 @@ import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; -import { FluxDispatcher, useLayoutEffect, useRef, useState } from "@webpack/common"; +import { FluxDispatcher, useLayoutEffect, useMemo, useRef, useState } from "@webpack/common"; import { ELEMENT_ID } from "../constants"; import { settings } from "../index"; @@ -160,6 +160,16 @@ export const Magnifier = ErrorBoundary.wrap(({ instance, size: i } }); + const imageSrc = useMemo(() => { + try { + const imageUrl = new URL(instance.props.src); + imageUrl.searchParams.set("animated", "true"); + return imageUrl.toString(); + } catch { + return instance.props.src; + } + }, [instance.props.src]); + if (!ready) return null; const box = element.current?.getBoundingClientRect(); @@ -203,7 +213,7 @@ export const Magnifier = ErrorBoundary.wrap(({ instance, size: i }} width={`${box.width * zoom.current}px`} height={`${box.height * zoom.current}px`} - src={instance.props.src + "?animated=true"} + src={imageSrc} alt="" /> )} From 8aa92873b9a401cfa83c47ae7e367330e9d623b2 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 2 May 2025 22:52:20 -0300 Subject: [PATCH 097/198] Fix MutualGroupDMs duplicate find --- src/plugins/mutualGroupDMs/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index f06cac0c..858a366c 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -88,7 +88,7 @@ export default definePlugin({ patches: [ { - find: ".MUTUAL_FRIENDS?(", + find: ".BOT_DATA_ACCESS?(", replacement: [ { match: /\i\.useEffect.{0,100}(\i)\[0\]\.section/, From 34269e2339ecae74625432062052ef27b6fbc3f2 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 2 May 2025 22:54:37 -0300 Subject: [PATCH 098/198] Bump to 1.12.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c75345d7..60fa7703 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.11.9", + "version": "1.12.0", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 074c587b074a6542ebde1af3266eb8bbe61ead54 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 2 May 2025 22:03:06 -0400 Subject: [PATCH 099/198] Fix +0 On Plugin Authors --- src/components/PluginSettings/PluginModal.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index 98f23fce..fa11674b 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -250,10 +250,9 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti count={plugin.authors.length} guildId={undefined} renderIcon={false} - max={6} showDefaultAvatarsForNullUsers showUserPopout - renderMoreUsers={renderMoreUsers} + renderMoreUsers={plugin.authors.length > 6 ? renderMoreUsers : undefined} renderUser={(user: User) => ( Date: Sat, 3 May 2025 00:26:41 -0400 Subject: [PATCH 100/198] Chore(Fix Case on VencordTab.tsx, Fix CSS for MoreUserTags & Platform Indicators) (#251) * Fix platform indicators CSS * format VencordTab.tsx Text Co-authored-by: Eazvy <57739965+Eazvy@users.noreply.github.com> * MoreUserTags CSS --------- Co-authored-by: Eazvy <57739965+Eazvy@users.noreply.github.com> --- src/components/VencordSettings/VencordTab.tsx | 6 +++--- src/equicordplugins/moreUserTags/styles.css | 11 +++++++++-- src/plugins/platformIndicators/style.css | 6 +----- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/VencordSettings/VencordTab.tsx b/src/components/VencordSettings/VencordTab.tsx index 53100ea9..b1908270 100644 --- a/src/components/VencordSettings/VencordTab.tsx +++ b/src/components/VencordSettings/VencordTab.tsx @@ -82,7 +82,7 @@ function EquicordSettings() { (!IS_DISCORD_DESKTOP || !isWindows ? { key: "frameless", - title: "Disable the window frame", + title: "Disable the Window Frame", note: "Requires a full restart", warning: { enabled: false }, } @@ -95,7 +95,7 @@ function EquicordSettings() { }), !IS_WEB && { key: "transparent", - title: "Enable window transparency.", + title: "Enable Window Transparency", note: "You need a theme that supports transparency or this will do nothing. Requires a full restart!", warning: { enabled: isWindows, @@ -112,7 +112,7 @@ function EquicordSettings() { }, IS_DISCORD_DESKTOP && { key: "disableMinSize", - title: "Disable minimum window size", + title: "Disable Minimum Window Size", note: "Requires a full restart", warning: { enabled: false }, }, diff --git a/src/equicordplugins/moreUserTags/styles.css b/src/equicordplugins/moreUserTags/styles.css index 41160325..bf6b34d4 100644 --- a/src/equicordplugins/moreUserTags/styles.css +++ b/src/equicordplugins/moreUserTags/styles.css @@ -1,10 +1,17 @@ +.vc-message-decorations-wrapper .vc-mut-message-tag { + margin-bottom: 1px; +} + +/* stylelint-disable-next-line no-descending-specificity */ .vc-mut-message-tag { /* Remove default margin from tags in messages */ margin-top: unset !important; /* Align with Discord default tags in messages */ - position: relative; - bottom: 0.01em; + /* stylelint-disable-next-line length-zero-no-unit */ + bottom: 0px; + top: -2px; + margin-right: 3px; } .vc-mut-message-verified { diff --git a/src/plugins/platformIndicators/style.css b/src/plugins/platformIndicators/style.css index a5566fdd..f05a4b61 100644 --- a/src/plugins/platformIndicators/style.css +++ b/src/plugins/platformIndicators/style.css @@ -6,10 +6,6 @@ } .vc-platform-indicator-profile { - background: rgb(var(--bg-overlay-color) / var(--bg-overlay-opacity-6)); - border: 1px solid var(--border-faint); - border-radius: var(--radius-xs); - border-color: var(--profile-body-border-color); margin: 0 1px; padding: 0 1px; } @@ -17,5 +13,5 @@ .vc-platform-indicator-message { position: relative; vertical-align: top; - top: 2px; + top: -1px; } From c9d8f3cc7256243afc963808d1958d0b24893733 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 3 May 2025 01:27:20 -0400 Subject: [PATCH 101/198] EquicordHelper: HideClanBages --- README.md | 3 +- src/equicordplugins/_core/equicordHelper.tsx | 27 +- src/equicordplugins/_misc/clanBadges.css | 3 + .../equicordCSS/css/betterauthapps.css | 1 - .../equicordCSS/css/betterstatuspicker.css | 1 - .../equicordCSS/css/discordicons.css | 1 - .../equicordCSS/css/gradientbuttons.css | 128 ----- .../equicordCSS/css/main.min.css | 487 ------------------ .../equicordCSS/css/nitrothemesfix.css | 1 - .../equicordCSS/css/settingsicons.css | 9 - .../equicordCSS/css/userreimagined.css | 1 - src/equicordplugins/equicordCSS/index.ts | 124 ----- 12 files changed, 29 insertions(+), 757 deletions(-) create mode 100644 src/equicordplugins/_misc/clanBadges.css delete mode 100644 src/equicordplugins/equicordCSS/css/betterauthapps.css delete mode 100644 src/equicordplugins/equicordCSS/css/betterstatuspicker.css delete mode 100644 src/equicordplugins/equicordCSS/css/discordicons.css delete mode 100644 src/equicordplugins/equicordCSS/css/gradientbuttons.css delete mode 100644 src/equicordplugins/equicordCSS/css/main.min.css delete mode 100644 src/equicordplugins/equicordCSS/css/nitrothemesfix.css delete mode 100644 src/equicordplugins/equicordCSS/css/settingsicons.css delete mode 100644 src/equicordplugins/equicordCSS/css/userreimagined.css delete mode 100644 src/equicordplugins/equicordCSS/index.ts diff --git a/README.md b/README.md index 5e77a949..9d0e1cf1 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-171 additional plugins +170 additional plugins ### All Platforms @@ -53,7 +53,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - DontFilterMe by Samwich - EmojiDumper by Cortex, Samwich, Woosh - Encryptcord by Inbestigator -- EquicordCSS by thororen, Panniku, Dablulite, Coolesding, MiniDiscordThemes, LuckFire, gold_me - EquicordHelper by thororen & nyx - Equissant by SomeAspy & thororen - ExportContacts by dat_insanity diff --git a/src/equicordplugins/_core/equicordHelper.tsx b/src/equicordplugins/_core/equicordHelper.tsx index 0d7e9108..7b50c43a 100644 --- a/src/equicordplugins/_core/equicordHelper.tsx +++ b/src/equicordplugins/_core/equicordHelper.tsx @@ -18,10 +18,26 @@ import "@equicordplugins/_misc/styles.css"; +import { definePluginSettings } from "@api/Settings"; +import { disableStyle, enableStyle } from "@api/Styles"; import { EquicordDevs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import { Forms } from "@webpack/common"; +import clanBadges from "../_misc/clanBadges.css?managed"; + +const settings = definePluginSettings({ + hideClanBadges: { + type: OptionType.BOOLEAN, + description: "Hide clan badges", + default: false, + onChange: value => { + if (value) enableStyle(clanBadges); + else disableStyle(clanBadges); + } + } +}); + export default definePlugin({ name: "EquicordHelper", description: "Fixes some misc issues with discord", @@ -31,6 +47,7 @@ export default definePlugin({ This Plugin is used for fixing misc issues with discord such as some crashes , + settings, required: true, patches: [ { @@ -46,5 +63,11 @@ export default definePlugin({ } ] } - ] + ], + start() { + if (settings.store.hideClanBadges) enableStyle(clanBadges); + }, + stop() { + if (settings.store.hideClanBadges) disableStyle(clanBadges); + } }); diff --git a/src/equicordplugins/_misc/clanBadges.css b/src/equicordplugins/_misc/clanBadges.css new file mode 100644 index 00000000..6e577ba7 --- /dev/null +++ b/src/equicordplugins/_misc/clanBadges.css @@ -0,0 +1,3 @@ +[class*="chipletContainerInner_"]:has([src *="/clan-badges/"]) { + display: none; +} diff --git a/src/equicordplugins/equicordCSS/css/betterauthapps.css b/src/equicordplugins/equicordCSS/css/betterauthapps.css deleted file mode 100644 index 8651840c..00000000 --- a/src/equicordplugins/equicordCSS/css/betterauthapps.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://dablulite.github.io/css-snippets/BetterAuthApps/import.css"); diff --git a/src/equicordplugins/equicordCSS/css/betterstatuspicker.css b/src/equicordplugins/equicordCSS/css/betterstatuspicker.css deleted file mode 100644 index f890b288..00000000 --- a/src/equicordplugins/equicordCSS/css/betterstatuspicker.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://dablulite.github.io/css-snippets/BetterStatusPicker/import.css"); diff --git a/src/equicordplugins/equicordCSS/css/discordicons.css b/src/equicordplugins/equicordCSS/css/discordicons.css deleted file mode 100644 index 3589a10d..00000000 --- a/src/equicordplugins/equicordCSS/css/discordicons.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://raw.githubusercontent.com/gold-me/DiscordIcons/master/DiscordIcons.theme.css"); diff --git a/src/equicordplugins/equicordCSS/css/gradientbuttons.css b/src/equicordplugins/equicordCSS/css/gradientbuttons.css deleted file mode 100644 index 900d64ea..00000000 --- a/src/equicordplugins/equicordCSS/css/gradientbuttons.css +++ /dev/null @@ -1,128 +0,0 @@ -/* stylelint-disable selector-class-pattern */ -/* stylelint-disable color-function-notation */ - -:root { - /* || Gradients */ - --gradient-special: 140deg, hsl(245deg, calc(var(--saturaton-factor, 1)*79%), 72%) 0%, hsl(287deg, calc(var(--saturaton-factor, 1)*80%), 70%) 100%; - --gradient-blurple: 140deg, hsl(235deg, calc(var(--saturation-factor, 1)*85%), 72%) 0%, hsl(235deg, calc(var(--saturation-factor, 1)*85%), 60%) 100%; - --gradient-green: 140deg, hsl(139deg, calc(var(--saturaton-factor, 1)*47%), 44%) 0%, hsl(139deg, calc(var(--saturaton-factor, 1)*66%), 24%) 100%; - --gradient-yellow: 140deg, hsl(38deg, calc(var(--saturaton-factor, 1)*96%), 54%) 0%, hsl(38deg, calc(var(--saturaton-factor, 1)*82%), 41%) 100%; - --gradient-red: 140deg, hsl(359deg, calc(var(--saturaton-factor, 1)*83%), 59%) 0%, hsl(359deg, calc(var(--saturaton-factor, 1)*54%), 37%) 100%; - --gradient-grey: 140deg, hsl(214deg, calc(var(--saturaton-factor, 1)*10%), 50%) 0%, hsl(216deg, calc(var(--saturaton-factor, 1)*11%), 26%) 100%; - - /* || Transitions */ - --button-transition: 0.1s linear; - --font-default: 500; - --font-hover: 525; - --fontsize-hover: 15px; - --transform-normal: scale(1); - --transform-hover: scale(1.15); - --button-transform-hover: scale(1.04); -} - -/* || Filled Buttons */ -.lookFilled-yCfaCM { - transform: var(--transform-normal); - transition: var(--button-transition); - background: var(--gradient); -} - -.lookFilled-yCfaCM:hover { - transform: var(--button-transform-hover); -} - -.lookFilled-yCfaCM[disabled] { - transform: none; -} - -.lookFilled-yCfaCM.colorBrand-I6CyqQ { - --gradient: linear-gradient(var(--gradient-blurple)); -} - -.lookFilled-yCfaCM.colorGreen-3y-Z79, -.lookFilled-yCfaCM.button_adcaac.buttonActive_adcaac { - --gradient: linear-gradient(var(--gradient-green)); -} - -.lookFilled-yCfaCM.colorYellow-Pgtmch { - --gradient: linear-gradient(var(--gradient-yellow)); -} - -.lookFilled-yCfaCM.colorRed-rQXKgM { - --gradient: linear-gradient(var(--gradient-red)); -} - -.lookFilled-yCfaCM.colorPrimary-2AuQVo, -.lookFilled-yCfaCM.colorGrey-2iAG-B, -.lookFilled-yCfaCM.buttonColor_adcaac { - --gradient: linear-gradient(var(--gradient-grey)); -} - -/* || Context Menus */ -.menu_d90b3d .item-1OdjEX:not(.hideInteraction-2jPGL_) { - font-weight: var(--font-default); - transition: var(--button-transition); -} - -.menu_d90b3d .item-1OdjEX:not(.hideInteraction-2jPGL_).focused-3qFvc8, -.menu_d90b3d .item-1OdjEX:not(.hideInteraction-2jPGL_):active { - font-size: var(--fontsize-hover); - font-weight: var(--font-hover); - background: var(--gradient); -} - -.menu_d90b3d .colorDefault-CDqZdO.focused-3qFvc8, -.menu_d90b3d .colorDefault-CDqZdO:active { - --gradient: linear-gradient(var(--gradient-blurple)); -} - -.menu_d90b3d .colorDanger-3n-KnP.focused-3qFvc8, -.menu_d90b3d .colorDanger-3n-KnP:active, -.menu_d90b3d #status-picker-dnd.focused-3qFvc8, -.menu_d90b3d #status-picker-dnd:active { - --gradient: linear-gradient(var(--gradient-red)); -} - -.menu_d90b3d .colorPremium-vwmYZQ.focused-3qFvc8, -.menu_d90b3d .colorPremium-vwmYZQ:active { - --gradient: linear-gradient(var(--gradient-special)); -} - -.menu_d90b3d #status-picker-online.focused-3qFvc8, -.menu_d90b3d #status-picker-online:active { - --gradient: linear-gradient(var(--gradient-green)); -} - -.menu_d90b3d #status-picker-idle.focused-3qFvc8, -.menu_d90b3d #status-picker-idle:active { - --gradient: linear-gradient(var(--gradient-yellow)); -} - -.menu_d90b3d #status-picker-invisible.focused-3qFvc8, -.menu_d90b3d #status-picker-invisible:active { - --gradient: linear-gradient(var(--gradient-grey)); -} - -/* || Message Actions */ -.wrapper_f7e168 .button_f7e168 { - background: var(--gradient); -} - -.wrapper_f7e168 .button_f7e168 img, -.wrapper_f7e168 .button_f7e168 svg { - transition: var(--button-transition); - transform: var(--transform-normal); -} - -.wrapper_f7e168 .button_f7e168:hover { - --gradient: linear-gradient(var(--gradient-blurple)); -} - -.wrapper_f7e168 .button_f7e168:hover svg { - transform: var(--transform-hover); - color: white; -} - -.wrapper_f7e168 .button_f7e168.dangerous_f7e168:hover { - --gradient: linear-gradient(var(--gradient-red)); -} diff --git a/src/equicordplugins/equicordCSS/css/main.min.css b/src/equicordplugins/equicordCSS/css/main.min.css deleted file mode 100644 index f52da4c2..00000000 --- a/src/equicordplugins/equicordCSS/css/main.min.css +++ /dev/null @@ -1,487 +0,0 @@ -/* stylelint-disable property-no-vendor-prefix */ -/* stylelint-disable selector-class-pattern */ - -:root { - --settingsicons: 1; - --si-size: 18px; - --si-gap: 14px; - --use-si: calc(var(--settingsicons, 1) / (var(--settingsicons, 1))); - --si-myaccount: url("https://minidiscordthemes.github.io/SettingsIcons/svg/myaccount.svg"); - --si-profilecustomization: url("https://minidiscordthemes.github.io/SettingsIcons/svg/profilecustomization.svg"); - --si-privacysafety: url("https://minidiscordthemes.github.io/SettingsIcons/svg/privacysafety.svg"); - --si-familycenter: url("https://minidiscordthemes.github.io/SettingsIcons/svg/familycenter.svg"); - --si-authorizedapps: url("https://minidiscordthemes.github.io/SettingsIcons/svg/authorizedapps.svg"); - --si-sessions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/sessions.svg"); - --si-connections: url("https://minidiscordthemes.github.io/SettingsIcons/svg/connections.svg"); - --si-settingsclips: url("https://minidiscordthemes.github.io/SettingsIcons/svg/settingsclips.svg"); - --si-friendrequests: url("https://minidiscordthemes.github.io/SettingsIcons/svg/friendrequests.svg"); - --si-discordnitro: url("https://minidiscordthemes.github.io/SettingsIcons/svg/discordnitro.svg"); - --si-nitroserverboost: url("https://minidiscordthemes.github.io/SettingsIcons/svg/nitroserverboost.svg"); - --si-subscriptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/subscriptions.svg"); - --si-libraryinventory: url("https://minidiscordthemes.github.io/SettingsIcons/svg/libraryinventory.svg"); - --si-billing: url("https://minidiscordthemes.github.io/SettingsIcons/svg/billing.svg"); - --si-appearance: url("https://minidiscordthemes.github.io/SettingsIcons/svg/appearance.svg"); - --si-accessibility: url("https://minidiscordthemes.github.io/SettingsIcons/svg/accessibility.svg"); - --si-voicevideo: url("https://minidiscordthemes.github.io/SettingsIcons/svg/voicevideo.svg"); - --si-textimages: url("https://minidiscordthemes.github.io/SettingsIcons/svg/textimages.svg"); - --si-notifications: url("https://minidiscordthemes.github.io/SettingsIcons/svg/notifications.svg"); - --si-keybinds: url("https://minidiscordthemes.github.io/SettingsIcons/svg/keybinds.svg"); - --si-language: url("https://minidiscordthemes.github.io/SettingsIcons/svg/language.svg"); - --si-windows: url("https://minidiscordthemes.github.io/SettingsIcons/svg/windows.svg"); - --si-streamermode: url("https://minidiscordthemes.github.io/SettingsIcons/svg/streamermode.svg"); - --si-rtcspeedtest: url("https://minidiscordthemes.github.io/SettingsIcons/svg/rtcspeedtest.svg"); - --si-advanced: url("https://minidiscordthemes.github.io/SettingsIcons/svg/advanced.svg"); - --si-activityprivacy: url("https://minidiscordthemes.github.io/SettingsIcons/svg/activityprivacy.svg"); - --si-gameactivity: url("https://minidiscordthemes.github.io/SettingsIcons/svg/gameactivity.svg"); - --si-overlay: url("https://minidiscordthemes.github.io/SettingsIcons/svg/overlay.svg"); - --si-changelog: url("https://minidiscordthemes.github.io/SettingsIcons/svg/changelog.svg"); - --si-merchandise: url("https://minidiscordthemes.github.io/SettingsIcons/svg/merchandise.svg"); - --si-hypesquadonline: url("https://minidiscordthemes.github.io/SettingsIcons/svg/hypesquadonline.svg"); - --si-powermodesettings: url("https://minidiscordthemes.github.io/SettingsIcons/svg/powermodesettings.svg"); - --si-experiments: url("https://minidiscordthemes.github.io/SettingsIcons/svg/experiments.svg"); - --si-developeroptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/developeroptions.svg"); - --si-hotspotoptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/hotspotoptions.svg"); - --si-dismissiblecontentoptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/dismissiblecontentoptions.svg"); - --si-startuptimings: url("https://minidiscordthemes.github.io/SettingsIcons/svg/startuptimings.svg"); - --si-paymentflowmodals: url("https://minidiscordthemes.github.io/SettingsIcons/svg/paymentflowmodals.svg"); - --si-textplayground: url("https://minidiscordthemes.github.io/SettingsIcons/svg/textplayground.svg"); - --si-textcomponent: url("https://minidiscordthemes.github.io/SettingsIcons/svg/textcomponent.svg"); - --si-logout: url("https://minidiscordthemes.github.io/SettingsIcons/svg/logout.svg"); - --si-equicordsettings: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordsettings.svg"); - --si-equicordplugins: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordplugins.svg"); - --si-equicordthemes: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordthemes.svg"); - --si-equicordupdater: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordupdater.svg"); - --si-equicordcloud: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordcloud.svg"); - --si-equicordsettingssync: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordsettingssync.svg"); - --si-equicordpatchhelper: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordpatchhelper.svg"); - --si-equibop: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vesktop.svg"); - --si-vesktop: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vesktop.svg"); - --si-overview: url("https://minidiscordthemes.github.io/SettingsIcons/svg/overview.svg"); - --si-roles: url("https://minidiscordthemes.github.io/SettingsIcons/svg/roles.svg"); - --si-emoji: url("https://minidiscordthemes.github.io/SettingsIcons/svg/emoji.svg"); - --si-stickers: url("https://minidiscordthemes.github.io/SettingsIcons/svg/stickers.svg"); - --si-soundboard: url("https://minidiscordthemes.github.io/SettingsIcons/svg/soundboard.svg"); - --si-widget: url("https://minidiscordthemes.github.io/SettingsIcons/svg/widget.svg"); - --si-guildtemplates: url("https://minidiscordthemes.github.io/SettingsIcons/svg/guildtemplates.svg"); - --si-vanityurl: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vanityurl.svg"); - --si-integrations: url("https://minidiscordthemes.github.io/SettingsIcons/svg/integrations.svg"); - --si-appdirectory: url("https://minidiscordthemes.github.io/SettingsIcons/svg/appdirectory.svg"); - --si-safety: url("https://minidiscordthemes.github.io/SettingsIcons/svg/safety.svg"); - --si-auditlog: url("https://minidiscordthemes.github.io/SettingsIcons/svg/auditlog.svg"); - --si-bans: url("https://minidiscordthemes.github.io/SettingsIcons/svg/bans.svg"); - --si-community: url("https://minidiscordthemes.github.io/SettingsIcons/svg/community.svg"); - --si-onboarding: url("https://minidiscordthemes.github.io/SettingsIcons/svg/onboarding.svg"); - --si-analytics: url("https://minidiscordthemes.github.io/SettingsIcons/svg/analytics.svg"); - --si-partner: url("https://minidiscordthemes.github.io/SettingsIcons/svg/partner.svg"); - --si-discovery: url("https://minidiscordthemes.github.io/SettingsIcons/svg/discovery.svg"); - --si-rolesubscriptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/rolesubscriptions.svg"); - --si-guildpremium: url("https://minidiscordthemes.github.io/SettingsIcons/svg/guildpremium.svg"); - --si-members: url("https://minidiscordthemes.github.io/SettingsIcons/svg/members.svg"); - --si-instantinvites: url("https://minidiscordthemes.github.io/SettingsIcons/svg/instantinvites.svg"); - --si-delete: url("https://minidiscordthemes.github.io/SettingsIcons/svg/delete.svg"); - --si-permissions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/permissions.svg"); - --si-default: url("https://minidiscordthemes.github.io/SettingsIcons/svg/default.svg"); -} - -.sidebarRegion_c25c6d { - flex-basis: calc(218px + var(--use-si)*(var(--si-size) + var(--si-gap))) !important -} - -.sidebar_c25c6d { - width: calc(218px + var(--use-si)*(var(--si-size) + var(--si-gap))) !important -} - -.sidebar_c25c6d :is(.item_a0 .icon_f7189e, .premiumLabel_ae3c77>svg, .premiumLabel_ae3c77 img, .tabBarItemContainer_e7c031>svg, .tabBarItemContainer_e7c031 img) { - transform: scaleX(calc(1 - var(--use-si))) -} - -.sidebar_c25c6d .side_a0 .item_a0 { - display: flex; - align-items: center -} - -.sidebar_c25c6d .side_a0 .item_a0::before { - content: ""; - flex: 0 0 auto; - width: calc(var(--use-si)*var(--si-size)); - height: calc(var(--use-si)*var(--si-size)); - margin-right: calc(var(--use-si)*var(--si-size)/2); - background: currentcolor; - z-index: 2; - -webkit-mask: var(--si-default) center/contain no-repeat; - mask: var(--si-default) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="My Account"]::before { - -webkit-mask: var(--si-myaccount) center/contain no-repeat; - mask: var(--si-myaccount) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Profile Customization"]::before { - -webkit-mask: var(--si-profilecustomization) center/contain no-repeat; - mask: var(--si-profilecustomization) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Privacy & Safety"]::before { - -webkit-mask: var(--si-privacysafety) center/contain no-repeat; - mask: var(--si-privacysafety) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Family Center"]::before { - -webkit-mask: var(--si-familycenter) center/contain no-repeat; - mask: var(--si-familycenter) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Authorized Apps"]::before { - -webkit-mask: var(--si-authorizedapps) center/contain no-repeat; - mask: var(--si-authorizedapps) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Sessions"]::before { - -webkit-mask: var(--si-sessions) center/contain no-repeat; - mask: var(--si-sessions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Connections"]::before { - -webkit-mask: var(--si-connections) center/contain no-repeat; - mask: var(--si-connections) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Settings Clips"]::before { - -webkit-mask: var(--si-settingsclips) center/contain no-repeat; - mask: var(--si-settingsclips) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Friend Requests"]::before { - -webkit-mask: var(--si-friendrequests) center/contain no-repeat; - mask: var(--si-friendrequests) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Discord Nitro"]::before { - -webkit-mask: var(--si-discordnitro) center/contain no-repeat; - mask: var(--si-discordnitro) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Nitro Server Boost"]::before { - -webkit-mask: var(--si-nitroserverboost) center/contain no-repeat; - mask: var(--si-nitroserverboost) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Subscriptions"]::before { - -webkit-mask: var(--si-subscriptions) center/contain no-repeat; - mask: var(--si-subscriptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Library Inventory"]::before { - -webkit-mask: var(--si-libraryinventory) center/contain no-repeat; - mask: var(--si-libraryinventory) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Billing"]::before { - -webkit-mask: var(--si-billing) center/contain no-repeat; - mask: var(--si-billing) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Appearance"]::before { - -webkit-mask: var(--si-appearance) center/contain no-repeat; - mask: var(--si-appearance) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Accessibility"]::before { - -webkit-mask: var(--si-accessibility) center/contain no-repeat; - mask: var(--si-accessibility) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Voice & Video"]::before { - -webkit-mask: var(--si-voicevideo) center/contain no-repeat; - mask: var(--si-voicevideo) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Text & Images"]::before { - -webkit-mask: var(--si-textimages) center/contain no-repeat; - mask: var(--si-textimages) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Notifications"]::before { - -webkit-mask: var(--si-notifications) center/contain no-repeat; - mask: var(--si-notifications) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Keybinds"]::before { - -webkit-mask: var(--si-keybinds) center/contain no-repeat; - mask: var(--si-keybinds) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Language"]::before { - -webkit-mask: var(--si-language) center/contain no-repeat; - mask: var(--si-language) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Windows"]::before { - -webkit-mask: var(--si-windows) center/contain no-repeat; - mask: var(--si-windows) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Streamer Mode"]::before { - -webkit-mask: var(--si-streamermode) center/contain no-repeat; - mask: var(--si-streamermode) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="RTC Speed Test"]::before { - -webkit-mask: var(--si-rtcspeedtest) center/contain no-repeat; - mask: var(--si-rtcspeedtest) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Advanced"]::before { - -webkit-mask: var(--si-advanced) center/contain no-repeat; - mask: var(--si-advanced) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Activity Privacy"]::before { - -webkit-mask: var(--si-activityprivacy) center/contain no-repeat; - mask: var(--si-activityprivacy) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Game Activity"]::before { - -webkit-mask: var(--si-gameactivity) center/contain no-repeat; - mask: var(--si-gameactivity) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Overlay"]::before { - -webkit-mask: var(--si-overlay) center/contain no-repeat; - mask: var(--si-overlay) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="changelog"]::before { - -webkit-mask: var(--si-changelog) center/contain no-repeat; - mask: var(--si-changelog) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="merchandise"]::before { - -webkit-mask: var(--si-merchandise) center/contain no-repeat; - mask: var(--si-merchandise) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Hypesquad Online"]::before { - -webkit-mask: var(--si-hypesquadonline) center/contain no-repeat; - mask: var(--si-hypesquadonline) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Powermode Settings"]::before { - -webkit-mask: var(--si-powermodesettings) center/contain no-repeat; - mask: var(--si-powermodesettings) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Experiments"]::before { - -webkit-mask: var(--si-experiments) center/contain no-repeat; - mask: var(--si-experiments) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Developer Options"]::before { - -webkit-mask: var(--si-developeroptions) center/contain no-repeat; - mask: var(--si-developeroptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Hotspot Options"]::before { - -webkit-mask: var(--si-hotspotoptions) center/contain no-repeat; - mask: var(--si-hotspotoptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Dismissible Content Options"]::before { - -webkit-mask: var(--si-dismissiblecontentoptions) center/contain no-repeat; - mask: var(--si-dismissiblecontentoptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="StartupTimings"]::before { - -webkit-mask: var(--si-startuptimings) center/contain no-repeat; - mask: var(--si-startuptimings) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Payment Flow Modals"]::before { - -webkit-mask: var(--si-paymentflowmodals) center/contain no-repeat; - mask: var(--si-paymentflowmodals) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Text Playground"]::before { - -webkit-mask: var(--si-textplayground) center/contain no-repeat; - mask: var(--si-textplayground) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Text Component"]::before { - -webkit-mask: var(--si-textcomponent) center/contain no-repeat; - mask: var(--si-textcomponent) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="logout"]::before { - -webkit-mask: var(--si-logout) center/contain no-repeat; - mask: var(--si-logout) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordSettings"]::before { - -webkit-mask: var(--si-equicordsettings) center/contain no-repeat; - mask: var(--si-equicordsettings) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordPlugins"]::before { - -webkit-mask: var(--si-equicordplugins) center/contain no-repeat; - mask: var(--si-equicordplugins) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordThemes"]::before { - -webkit-mask: var(--si-equicordthemes) center/contain no-repeat; - mask: var(--si-equicordthemes) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordUpdater"]::before { - -webkit-mask: var(--si-equicordupdater) center/contain no-repeat; - mask: var(--si-equicordupdater) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordCloud"]::before { - -webkit-mask: var(--si-equicordcloud) center/contain no-repeat; - mask: var(--si-equicordcloud) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordSettingsSync"]::before { - -webkit-mask: var(--si-equicordsettingssync) center/contain no-repeat; - mask: var(--si-equicordsettingssync) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordPatchHelper"]::before { - -webkit-mask: var(--si-equicordpatchhelper) center/contain no-repeat; - mask: var(--si-equicordpatchhelper) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Equibop"]::before { - -webkit-mask: var(--si-equibop) center/contain no-repeat; - mask: var(--si-equibop) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Vesktop"]::before { - -webkit-mask: var(--si-vesktop) center/contain no-repeat; - mask: var(--si-vesktop) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="OVERVIEW"]::before { - -webkit-mask: var(--si-overview) center/contain no-repeat; - mask: var(--si-overview) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="ROLES"]::before { - -webkit-mask: var(--si-roles) center/contain no-repeat; - mask: var(--si-roles) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EMOJI"]::before { - -webkit-mask: var(--si-emoji) center/contain no-repeat; - mask: var(--si-emoji) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="STICKERS"]::before { - -webkit-mask: var(--si-stickers) center/contain no-repeat; - mask: var(--si-stickers) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="SOUNDBOARD"]::before { - -webkit-mask: var(--si-soundboard) center/contain no-repeat; - mask: var(--si-soundboard) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="WIDGET"]::before { - -webkit-mask: var(--si-widget) center/contain no-repeat; - mask: var(--si-widget) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="GUILD_TEMPLATES"]::before { - -webkit-mask: var(--si-guildtemplates) center/contain no-repeat; - mask: var(--si-guildtemplates) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="VANITY_URL"]::before { - -webkit-mask: var(--si-vanityurl) center/contain no-repeat; - mask: var(--si-vanityurl) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="INTEGRATIONS"]::before { - -webkit-mask: var(--si-integrations) center/contain no-repeat; - mask: var(--si-integrations) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="APP_DIRECTORY"]::before { - -webkit-mask: var(--si-appdirectory) center/contain no-repeat; - mask: var(--si-appdirectory) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="SAFETY"]::before { - -webkit-mask: var(--si-safety) center/contain no-repeat; - mask: var(--si-safety) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="AUDIT_LOG"]::before { - -webkit-mask: var(--si-auditlog) center/contain no-repeat; - mask: var(--si-auditlog) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="BANS"]::before { - -webkit-mask: var(--si-bans) center/contain no-repeat; - mask: var(--si-bans) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="COMMUNITY"]::before { - -webkit-mask: var(--si-community) center/contain no-repeat; - mask: var(--si-community) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="ONBOARDING"]::before { - -webkit-mask: var(--si-onboarding) center/contain no-repeat; - mask: var(--si-onboarding) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="ANALYTICS"]::before { - -webkit-mask: var(--si-analytics) center/contain no-repeat; - mask: var(--si-analytics) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="PARTNER"]::before { - -webkit-mask: var(--si-partner) center/contain no-repeat; - mask: var(--si-partner) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="DISCOVERY"]::before { - -webkit-mask: var(--si-discovery) center/contain no-repeat; - mask: var(--si-discovery) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="ROLE_SUBSCRIPTIONS"]::before { - -webkit-mask: var(--si-rolesubscriptions) center/contain no-repeat; - mask: var(--si-rolesubscriptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="GUILD_PREMIUM"]::before { - -webkit-mask: var(--si-guildpremium) center/contain no-repeat; - mask: var(--si-guildpremium) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="MEMBERS"]::before { - -webkit-mask: var(--si-members) center/contain no-repeat; - mask: var(--si-members) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="INSTANT_INVITES"]::before { - -webkit-mask: var(--si-instantinvites) center/contain no-repeat; - mask: var(--si-instantinvites) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="DELETE"]::before { - -webkit-mask: var(--si-delete) center/contain no-repeat; - mask: var(--si-delete) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="PERMISSIONS"]::before { - -webkit-mask: var(--si-permissions) center/contain no-repeat; - mask: var(--si-permissions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0>div { - flex: 1 1 auto -} diff --git a/src/equicordplugins/equicordCSS/css/nitrothemesfix.css b/src/equicordplugins/equicordCSS/css/nitrothemesfix.css deleted file mode 100644 index 87cd5810..00000000 --- a/src/equicordplugins/equicordCSS/css/nitrothemesfix.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://raw.githubusercontent.com/coolesding/snippets/main/import/fixnitrothemes.css"); diff --git a/src/equicordplugins/equicordCSS/css/settingsicons.css b/src/equicordplugins/equicordCSS/css/settingsicons.css deleted file mode 100644 index 5e0a8b7d..00000000 --- a/src/equicordplugins/equicordCSS/css/settingsicons.css +++ /dev/null @@ -1,9 +0,0 @@ -@import url("https://raw.githubusercontent.com/Equicord/Equicord/main/src/equicordplugins/equicordCSS/css/main.min.css"); - -/* https://github.com/MiniDiscordThemes/SettingsIcons#customisation */ - -:root { - --settingsicons: 1; - --si-size: 18px; - --si-gap: 14px; -} diff --git a/src/equicordplugins/equicordCSS/css/userreimagined.css b/src/equicordplugins/equicordCSS/css/userreimagined.css deleted file mode 100644 index fcfd98d1..00000000 --- a/src/equicordplugins/equicordCSS/css/userreimagined.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://dablulite.github.io/css-snippets/UserReimagined/import.css"); diff --git a/src/equicordplugins/equicordCSS/index.ts b/src/equicordplugins/equicordCSS/index.ts deleted file mode 100644 index 15dd4a7b..00000000 --- a/src/equicordplugins/equicordCSS/index.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 Vendicated and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ - -// Import required modules and components -import { definePluginSettings } from "@api/Settings"; -import { disableStyle, enableStyle } from "@api/Styles"; -import { EquicordDevs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; - -// Importing the style managed fixes on and off switch -import betterauthapps from "./css/betterauthapps.css?managed"; -import betterstatuspicker from "./css/betterstatuspicker.css?managed"; -import discordicons from "./css/discordicons.css?managed"; -import gradientbuttons from "./css/gradientbuttons.css?managed"; -import nitrothemesfix from "./css/nitrothemesfix.css?managed"; -import settingsicons from "./css/settingsicons.css?managed"; -import userreimagined from "./css/userreimagined.css?managed"; - -// Forcing restartNeeded: true to not overcomplicate the live update of the settings using FluxDispatcher and making it complex -const settings = definePluginSettings({ - betterAuthApps: { - type: OptionType.BOOLEAN, - description: "Enable Better Auth Apps CSS", - restartNeeded: true, - default: false - }, - betterStatusPicker: { - type: OptionType.BOOLEAN, - description: "Enable Better Status Picker CSS", - restartNeeded: true, - default: false - }, - discordicons: { - type: OptionType.BOOLEAN, - description: "Enable Discord Icons CSS", - restartNeeded: true, - default: false - }, - gradientButtons: { - type: OptionType.BOOLEAN, - description: "Enable Gradient Buttons CSS", - restartNeeded: true, - default: false - }, - nitroThemesFix: { - type: OptionType.BOOLEAN, - description: "Enable Fix Nitro Themes CSS", - restartNeeded: true, - default: false - }, - settingsIcons: { - type: OptionType.BOOLEAN, - description: "Enable Settings Icons CSS", - restartNeeded: true, - default: false - }, - userReimagined: { - type: OptionType.BOOLEAN, - description: "Enable User Reimagined CSS", - restartNeeded: true, - default: false - } -}); - -let settingsArray: Array = []; -let cssArray: Array = []; - -export default definePlugin({ - name: "EquicordCSS", - description: "CSS for Equicord users. You will need to look at the settings.", - authors: [EquicordDevs.thororen, EquicordDevs.Panniku], - dependencies: ["ThemeAttributes"], - settings, - start() { - - // Push variables to array to iterate on start() and stop() - settingsArray.push( - settings.store.betterAuthApps, - settings.store.betterStatusPicker, - settings.store.discordicons, - settings.store.gradientButtons, - settings.store.nitroThemesFix, - settings.store.settingsIcons, - settings.store.userReimagined - ); - cssArray.push( - betterauthapps, - betterstatuspicker, - discordicons, - gradientbuttons, - nitrothemesfix, - settingsicons, - userreimagined - ); - - settingsArray.forEach((s, i) => { - if (s) enableStyle(cssArray[i]); - }); - }, - stop() { - - settingsArray.forEach((s, i) => { - if (s) disableStyle(cssArray[i]); - }); - - settingsArray = []; - cssArray = []; - } -}); From 15fa0ff7a7351c7217793f30ad3a5d32846ff873 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 3 May 2025 08:20:32 -0300 Subject: [PATCH 102/198] PlainFolderIcon: Fix folder colors changing with other themes --- src/plugins/plainFolderIcon/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/plainFolderIcon/style.css b/src/plugins/plainFolderIcon/style.css index 70a49078..3e2992fc 100644 --- a/src/plugins/plainFolderIcon/style.css +++ b/src/plugins/plainFolderIcon/style.css @@ -6,5 +6,5 @@ transform: translateZ(0); /* The new icons are fully transparent. Add a sane default to match the old behavior */ - background-color: color-mix(in oklab, var(--custom-folder-color, var(--bg-brand)) 30%, var(--background-surface-higher) 70%); + background-color: color-mix(in oklab, var(--custom-folder-color, var(--bg-brand)) 40%, transparent); } From 578aa5f107c03d7cd96f18fa6b3546fe836fd04e Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 3 May 2025 08:53:58 -0300 Subject: [PATCH 103/198] Experiments: Fix client crash with new experiment embed --- src/plugins/experiments/index.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 49ae1ab7..2482d051 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -109,10 +109,17 @@ export default definePlugin({ // Enable experiment embed on sent experiment links { find: "dev://experiment/", - replacement: { - match: /\i\.isStaff\(\)/, - replace: "true" - } + replacement: [ + { + match: /\i\.isStaff\(\)/, + replace: "true" + }, + // Fix some tricky experiments name causing a client crash + { + match: /.getRegisteredExperiments\(\)(?<=(\i)=.+?).+?if\(null==(\i)(?=\)return null;)/, + replace: "$&||!Object.hasOwn($1,$2)" + } + ] }, ], From fa7f7f2d81566a49b8d44b66fa24186ca183df36 Mon Sep 17 00:00:00 2001 From: Crxaw <48805031+sitescript@users.noreply.github.com> Date: Sun, 4 May 2025 02:00:29 +0100 Subject: [PATCH 104/198] Update SpotifyLyrics!! (#252) * 1 * Update SpotifyLyrics!! * Update pnpm-lock.yaml --------- Co-authored-by: Crxa Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- .../providers/lrclibAPI/index.ts | 2 +- .../spotifyLyrics/providers/store.ts | 48 ++++--------------- .../spotifyLyrics/settings.tsx | 13 +++-- 3 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/equicordplugins/spotifyLyrics/providers/lrclibAPI/index.ts b/src/equicordplugins/spotifyLyrics/providers/lrclibAPI/index.ts index 3da1fabf..e9981763 100644 --- a/src/equicordplugins/spotifyLyrics/providers/lrclibAPI/index.ts +++ b/src/equicordplugins/spotifyLyrics/providers/lrclibAPI/index.ts @@ -49,7 +49,7 @@ export async function getLyricsLrclib(track: Track): Promise if (!data.syncedLyrics) return null; const lyrics = data.syncedLyrics; - const lines = lyrics.split("\n"); + const lines = lyrics.split("\n").filter(line => line.trim() !== ""); return { useLyric: Provider.Lrclib, diff --git a/src/equicordplugins/spotifyLyrics/providers/store.ts b/src/equicordplugins/spotifyLyrics/providers/store.ts index 574eedb0..580af799 100644 --- a/src/equicordplugins/spotifyLyrics/providers/store.ts +++ b/src/equicordplugins/spotifyLyrics/providers/store.ts @@ -102,38 +102,20 @@ export const SpotifyLrcStore = proxyLazyWebpack(() => { store.emitChange(); return; } - // stops spamming noftications for translation when there is no lyrics x1 + if (provider === Provider.Translated || provider === Provider.Romanized) { - if (!currentInfo?.useLyric || !currentInfo.lyricsVersions[currentInfo.useLyric]) { - console.log("Failed to Translate"); - const now = Date.now(); - if (!window.__lastTranslateFailure) { - window.__lastTranslateFailure = now; - } else if (now - window.__lastTranslateFailure < 120000) { // 2 minutes - window.__lastTranslateFailure = null; - return null; - } else { - window.__lastTranslateFailure = now; - } - return null; + if (!currentInfo?.lyricsVersions[Provider.Spotify] && !currentInfo?.lyricsVersions[Provider.Lrclib]) { + showNotif("No lyrics", `No lyrics to ${provider === Provider.Translated ? "translate" : "romanize"}`); + return; } const fetcher = provider === Provider.Translated ? translateLyrics : romanizeLyrics; const fetchResult = await fetcher(currentInfo.lyricsVersions[currentInfo.useLyric]); - // stops spamming noftications for when there is no lyrics / cannot be translated x2 + if (!fetchResult) { - console.log("Lyrics fetch failed", `Failed to fetch ${provider === Provider.Translated ? "translation" : "romanization"}`); - const now = Date.now(); - if (!window.__lastTranslateFailure) { - window.__lastTranslateFailure = now; - } else if (now - window.__lastTranslateFailure < 120000) { // 2 minutes - window.__lastTranslateFailure = null; - return null; - } else { - window.__lastTranslateFailure = now; - } - return null; + showNotif("Lyrics fetch failed", `Failed to fetch ${provider === Provider.Translated ? "translation" : "romanization"}`); + return; } store.lyricsInfo = { @@ -141,7 +123,7 @@ export const SpotifyLrcStore = proxyLazyWebpack(() => { useLyric: provider, lyricsVersions: { ...currentInfo.lyricsVersions, - [Provider.Translated]: fetchResult + [provider]: fetchResult } }; @@ -152,19 +134,9 @@ export const SpotifyLrcStore = proxyLazyWebpack(() => { } const newLyricsInfo = await lyricFetchers[e.provider](store.track!); - // stops spamming noftications for when there is no lyrics / cannot be translated x3 if (!newLyricsInfo) { - console.log("Lyrics fetch failed", `Failed to fetch ${e.provider} lyrics`); - const now = Date.now(); - if (!window.__lastLyricsFetchFailure) { - window.__lastLyricsFetchFailure = now; - } else if (now - window.__lastLyricsFetchFailure < 120000) { // 2 minutes - window.__lastLyricsFetchFailure = null; - return null; - } else { - window.__lastLyricsFetchFailure = now; - } - return null; + showNotif("Lyrics fetch failed", `Failed to fetch ${e.provider} lyrics`); + return; } store.lyricsInfo = newLyricsInfo; diff --git a/src/equicordplugins/spotifyLyrics/settings.tsx b/src/equicordplugins/spotifyLyrics/settings.tsx index 0a3ad011..032b5a8e 100644 --- a/src/equicordplugins/spotifyLyrics/settings.tsx +++ b/src/equicordplugins/spotifyLyrics/settings.tsx @@ -8,7 +8,7 @@ import { definePluginSettings } from "@api/Settings"; import { makeRange, SettingSliderComponent } from "@components/PluginSettings/components"; import { useAwaiter } from "@utils/react"; import { OptionType } from "@utils/types"; -import { Button, showToast, Text, Toasts } from "@webpack/common"; +import { Button, showToast, Text, Toasts, useMemo } from "@webpack/common"; import { clearLyricsCache, getLyricsCount, removeTranslations } from "./api"; import { Lyrics } from "./components/lyrics"; @@ -24,10 +24,13 @@ const sliderOptions = { function Details() { const { lyricsInfo } = useLyrics(); - const [count, error, loading] = useAwaiter(getLyricsCount, { - onError: () => console.error("Failed to get lyrics count"), - fallbackValue: null, - }); + const [count, error, loading] = useAwaiter( + useMemo(() => getLyricsCount, []), + { + onError: () => console.error("Failed to get lyrics count"), + fallbackValue: null, + } + ); return ( <> From a0dfaf056ac1a1f43fb83f05ea8b7c7e4fbef678 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 3 May 2025 21:02:03 -0400 Subject: [PATCH 105/198] NixOS Dispatch --- .github/workflows/nixosBuild.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nixosBuild.yml b/.github/workflows/nixosBuild.yml index 02f990e1..d2792eb5 100644 --- a/.github/workflows/nixosBuild.yml +++ b/.github/workflows/nixosBuild.yml @@ -1,5 +1,6 @@ name: NixOS Build on: + workflow_dispatch: schedule: - cron: 0 0 * * * env: From d2a0324647200ad6579df04916303432ee3892c0 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 4 May 2025 00:21:12 -0400 Subject: [PATCH 106/198] Fix MoreUserTags Settings --- src/components/PluginSettings/PluginModal.tsx | 12 +-- src/equicordplugins/moreUserTags/settings.tsx | 98 ++++++++++++------- .../components/NotificationComponent.tsx | 6 +- 3 files changed, 67 insertions(+), 49 deletions(-) diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index fa11674b..69a2273f 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -179,20 +179,18 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti } } - function renderMoreUsers(_label: string, count: number) { - const sliceCount = plugin.authors.length - count; - const sliceStart = plugin.authors.length - sliceCount; - const sliceEnd = sliceStart + plugin.authors.length - count; + function renderMoreUsers(_label: string) { + const remainingAuthors = plugin.authors.slice(6); return ( - u.name).join(", ")}> + u.name).join(", ")}> {({ onMouseEnter, onMouseLeave }) => (
- +{sliceCount} + +{remainingAuthors.length}
)}
@@ -252,7 +250,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti renderIcon={false} showDefaultAvatarsForNullUsers showUserPopout - renderMoreUsers={plugin.authors.length > 6 ? renderMoreUsers : undefined} + renderMoreUsers={renderMoreUsers} renderUser={(user: User) => ( - {tags.map(t => ( - - - - {({ onMouseEnter, onMouseLeave }) => ( -
- {t.displayName} Tag -
- )} -
-
- - tagSettings[t.name].text = v} - className={Margins.bottom16} - /> - - tagSettings[t.name].showInChat = v} - hideBorder +
+ {tags.map(t => ( + - Show in messages - + + + {({ onMouseEnter, onMouseLeave }) => ( +
+ {t.displayName} Tag +
+ )} +
+
- tagSettings[t.name].showInNotChat = v} - hideBorder - > - Show in member list and profiles - -
- ))} +
+ + Example: + + +
+ + tagSettings[t.name].text = v} + className={Margins.bottom16} + /> + + tagSettings[t.name].showInChat = v} + hideBorder + > + Show in messages + + + tagSettings[t.name].showInNotChat = v} + hideBorder + > + Show in member list and profiles + + + ))} +
); } diff --git a/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx b/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx index 0f6adb75..207d99bb 100644 --- a/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx +++ b/src/equicordplugins/toastNotifications/components/NotificationComponent.tsx @@ -44,11 +44,9 @@ export default ErrorBoundary.wrap(function NotificationComponent({ let renderBody: boolean = true; let footer: boolean = false; - if (attachments > 0) - footer = true; + if (attachments > 0) footer = true; - if (body === "") - renderBody = false; + if (body === "") renderBody = false; // Precompute appearance settings. const AppearanceSettings = { From 0327fd52040b54139a09cb85af1a4c5239e73af0 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 4 May 2025 00:27:27 -0400 Subject: [PATCH 107/198] Test New Readme Header Fix Readme Image Finally Revert "Test New Readme Header" This reverts commit 06ae3c33e42db6572ab0886e8017d813e4dc5c2b. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d0e1cf1..7209e2f3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Equicord [Equicord](https://github.com/Equicord/Equicord) +# [Equicord](https://github.com/Equicord/Equicord) Equicord [![Equibop](https://img.shields.io/badge/Equibop-grey?style=flat)](https://github.com/Equicord/Equibop) [![Tests](https://github.com/Equicord/Equicord/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/Equicord/Equicord/actions/workflows/test.yml) From 02acf08ab22a73824b2c12005918ceb58cc73422 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 4 May 2025 01:41:56 -0400 Subject: [PATCH 108/198] spotifyActivityToggle --- README.md | 3 +- .../spotifyActivityToggle/index.tsx | 84 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/equicordplugins/spotifyActivityToggle/index.tsx diff --git a/README.md b/README.md index 7209e2f3..94689378 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,8 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - Signature by Ven, Rini, ImBanana, KrystalSkull - Slap by Korbo - SoundBoardLogger by Moxxie, fres, echo, maintained by thororen -- - SplitLargeMessages by Reycko +- SplitLargeMessages by Reycko +- SpotifyActivityToggle by thororen - SpotifyLyrics by Joona - StatsfmPresence by Crxa - StatusPresets by iamme diff --git a/src/equicordplugins/spotifyActivityToggle/index.tsx b/src/equicordplugins/spotifyActivityToggle/index.tsx new file mode 100644 index 00000000..768e0b94 --- /dev/null +++ b/src/equicordplugins/spotifyActivityToggle/index.tsx @@ -0,0 +1,84 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import ErrorBoundary from "@components/ErrorBoundary"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { findComponentByCodeLazy } from "@webpack"; +import { Constants, React, RestAPI } from "@webpack/common"; + +const Button = findComponentByCodeLazy(".NONE,disabled:", ".PANEL_BUTTON"); + +function makeSpotifyIcon(enabled: boolean) { + return ( + + + + ); +} + +function SpotifyActivityToggleButton() { + const { spotifyConnection } = settings.store; + const setSpotifyConnection = value => settings.store.spotifyConnection = value; + + return ( + } + {(nitroThemeEnabled) && } +
+ + )} +
+ ); +} + +export function ResetThemeColorComponent() { + return ( + + ); +} diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index 2b77d00d..984318e2 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -7,104 +7,21 @@ import "./clientTheme.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import { Devs } from "@utils/constants"; -import { Margins } from "@utils/margins"; -import { classes } from "@utils/misc"; import definePlugin, { OptionType, StartAt } from "@utils/types"; -import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; -import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common"; -const cl = classNameFactory("vc-clientTheme-"); +import { ResetThemeColorComponent, ThemeSettingsComponent } from "./components/Settings"; +import { disableClientTheme, startClientTheme } from "./utils/styleUtils"; -const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)"); - -const colorPresets = [ - "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", - "#3A483D", "#344242", "#313D4B", "#2D2F47", "#322B42", - "#3C2E42", "#422938", "#b6908f", "#bfa088", "#d3c77d", - "#86ac86", "#88aab3", "#8693b5", "#8a89ba", "#ad94bb", -]; - -function onPickColor(color: number) { - const hexColor = color.toString(16).padStart(6, "0"); - - settings.store.color = hexColor; - updateColorVars(hexColor); -} - -const saveClientTheme = findByCodeLazy('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"==='); - -function setTheme(theme: string) { - saveClientTheme({ theme }); -} - -const NitroThemeStore = findStoreLazy("ClientThemesBackgroundStore"); - -function ThemeSettings() { - const theme = useStateFromStores([ThemeStore], () => ThemeStore.theme); - const isLightTheme = theme === "light"; - const oppositeTheme = isLightTheme ? "dark" : "light"; - - const nitroTheme = useStateFromStores([NitroThemeStore], () => NitroThemeStore.gradientPreset); - const nitroThemeEnabled = nitroTheme !== undefined; - - const selectedLuminance = relativeLuminance(settings.store.color); - - let contrastWarning = false, fixableContrast = true; - if ((isLightTheme && selectedLuminance < 0.26) || !isLightTheme && selectedLuminance > 0.12) - contrastWarning = true; - if (selectedLuminance < 0.26 && selectedLuminance > 0.12) - fixableContrast = false; - // light mode with values greater than 65 leads to background colors getting crushed together and poor text contrast for muted channels - if (isLightTheme && selectedLuminance > 0.65) { - contrastWarning = true; - fixableContrast = false; - } - - return ( -
-
-
- Theme Color - Add a color to your Discord client theme -
- -
- {(contrastWarning || nitroThemeEnabled) && (<> - -
-
- Warning, your theme won't look good: - {contrastWarning && Selected color won't contrast well with text} - {nitroThemeEnabled && Nitro themes aren't supported} -
- {(contrastWarning && fixableContrast) && } - {(nitroThemeEnabled) && } -
- )} -
- ); -} - -const settings = definePluginSettings({ +export const settings = definePluginSettings({ color: { type: OptionType.COMPONENT, default: "313338", - component: ThemeSettings + component: ThemeSettingsComponent }, resetColor: { type: OptionType.COMPONENT, - component: () => ( - - ) + component: ResetThemeColorComponent } }); @@ -115,185 +32,6 @@ export default definePlugin({ settings, startAt: StartAt.DOMContentLoaded, - async start() { - updateColorVars(settings.store.color); - - const styles = await getStyles(); - generateColorOffsets(styles); - generateLightModeFixes(styles); - }, - - stop() { - document.getElementById("clientThemeVars")?.remove(); - document.getElementById("clientThemeOffsets")?.remove(); - document.getElementById("clientThemeLightModeFixes")?.remove(); - } + start: () => startClientTheme(settings.store.color), + stop: disableClientTheme }); - -const visualRefreshVariableRegex = /(--neutral-\d{1,3}-hsl):.*?(\S*)%;/g; -const oldVariableRegex = /(--primary-\d{3}-hsl):.*?(\S*)%;/g; -const lightVariableRegex = /^--primary-[1-5]\d{2}-hsl/g; -const darkVariableRegex = /^--primary-[5-9]\d{2}-hsl/g; - -// generates variables per theme by: -// - matching regex (so we can limit what variables are included in light/dark theme, otherwise text becomes unreadable) -// - offset from specified center (light/dark theme get different offsets because light uses 100 for background-primary, while dark uses 600) -function genThemeSpecificOffsets(variableLightness: Record, regex: RegExp | null, centerVariable: string): string { - return Object.entries(variableLightness).filter(([key]) => regex == null || key.search(regex) > -1) - .map(([key, lightness]) => { - const lightnessOffset = lightness - variableLightness[centerVariable]; - const plusOrMinus = lightnessOffset >= 0 ? "+" : "-"; - return `${key}: var(--theme-h) var(--theme-s) calc(var(--theme-l) ${plusOrMinus} ${Math.abs(lightnessOffset).toFixed(2)}%);`; - }) - .join("\n"); -} - -function generateColorOffsets(styles) { - const oldVariableLightness = {} as Record; - const visualRefreshVariableLightness = {} as Record; - - // Get lightness values of --primary variables - for (const [, variable, lightness] of styles.matchAll(oldVariableRegex)) { - oldVariableLightness[variable] = parseFloat(lightness); - } - - for (const [, variable, lightness] of styles.matchAll(visualRefreshVariableRegex)) { - visualRefreshVariableLightness[variable] = parseFloat(lightness); - } - - createStyleSheet("clientThemeOffsets", [ - `.theme-light {\n ${genThemeSpecificOffsets(oldVariableLightness, lightVariableRegex, "--primary-345-hsl")} \n}`, - `.theme-dark {\n ${genThemeSpecificOffsets(oldVariableLightness, darkVariableRegex, "--primary-600-hsl")} \n}`, - `.visual-refresh.theme-light {\n ${genThemeSpecificOffsets(visualRefreshVariableLightness, null, "--neutral-2-hsl")} \n}`, - `.visual-refresh.theme-dark {\n ${genThemeSpecificOffsets(visualRefreshVariableLightness, null, "--neutral-69-hsl")} \n}`, - ].join("\n\n")); -} - -function generateLightModeFixes(styles: string) { - const groupLightUsesW500Regex = /\.theme-light[^{]*\{[^}]*var\(--white-500\)[^}]*}/gm; - // get light capturing groups that mention --white-500 - const relevantStyles = [...styles.matchAll(groupLightUsesW500Regex)].flat(); - - const groupBackgroundRegex = /^([^{]*)\{background:var\(--white-500\)/m; - const groupBackgroundColorRegex = /^([^{]*)\{background-color:var\(--white-500\)/m; - // find all capturing groups that assign background or background-color directly to w500 - const backgroundGroups = mapReject(relevantStyles, entry => captureOne(entry, groupBackgroundRegex)).join(",\n"); - const backgroundColorGroups = mapReject(relevantStyles, entry => captureOne(entry, groupBackgroundColorRegex)).join(",\n"); - // create css to reassign them to --primary-100 - const reassignBackgrounds = `${backgroundGroups} {\n background: var(--primary-100) \n}`; - const reassignBackgroundColors = `${backgroundColorGroups} {\n background-color: var(--primary-100) \n}`; - - const groupBgVarRegex = /\.theme-light\{([^}]*--[^:}]*(?:background|bg)[^:}]*:var\(--white-500\)[^}]*)\}/m; - const bgVarRegex = /^(--[^:]*(?:background|bg)[^:]*):var\(--white-500\)/m; - // get all global variables used for backgrounds - const lightVars = mapReject(relevantStyles, style => captureOne(style, groupBgVarRegex)) // get the insides of capture groups that have at least one background var with w500 - .map(str => str.split(";")).flat(); // captureGroupInsides[] -> cssRule[] - const lightBgVars = mapReject(lightVars, variable => captureOne(variable, bgVarRegex)); // remove vars that aren't for backgrounds or w500 - // create css to reassign every var - const reassignVariables = `.theme-light {\n ${lightBgVars.map(variable => `${variable}: var(--primary-100);`).join("\n")} \n}`; - - createStyleSheet("clientThemeLightModeFixes", [ - reassignBackgrounds, - reassignBackgroundColors, - reassignVariables, - ].join("\n\n")); -} - -function captureOne(str, regex) { - const result = str.match(regex); - return (result === null) ? null : result[1]; -} - -function mapReject(arr, mapFunc) { - return arr.map(mapFunc).filter(Boolean); -} - -function updateColorVars(color: string) { - const { hue, saturation, lightness } = hexToHSL(color); - - let style = document.getElementById("clientThemeVars"); - if (!style) - style = createStyleSheet("clientThemeVars"); - - style.textContent = `:root { - --theme-h: ${hue}; - --theme-s: ${saturation}%; - --theme-l: ${lightness}%; - }`; -} - -function createStyleSheet(id, content = "") { - const style = document.createElement("style"); - style.setAttribute("id", id); - style.textContent = content.split("\n").map(line => line.trim()).join("\n"); - document.body.appendChild(style); - return style; -} - -// returns all of discord's native styles in a single string -async function getStyles(): Promise { - let out = ""; - const styleLinkNodes = document.querySelectorAll('link[rel="stylesheet"]'); - for (const styleLinkNode of styleLinkNodes) { - const cssLink = styleLinkNode.getAttribute("href"); - if (!cssLink) continue; - - const res = await fetch(cssLink); - out += await res.text(); - } - return out; -} - -// https://css-tricks.com/converting-color-spaces-in-javascript/ -function hexToHSL(hexCode: string) { - // Hex => RGB normalized to 0-1 - const r = parseInt(hexCode.substring(0, 2), 16) / 255; - const g = parseInt(hexCode.substring(2, 4), 16) / 255; - const b = parseInt(hexCode.substring(4, 6), 16) / 255; - - // RGB => HSL - const cMax = Math.max(r, g, b); - const cMin = Math.min(r, g, b); - const delta = cMax - cMin; - - let hue: number, saturation: number, lightness: number; - - lightness = (cMax + cMin) / 2; - - if (delta === 0) { - // If r=g=b then the only thing that matters is lightness - hue = 0; - saturation = 0; - } else { - // Magic - saturation = delta / (1 - Math.abs(2 * lightness - 1)); - - if (cMax === r) - hue = ((g - b) / delta) % 6; - else if (cMax === g) - hue = (b - r) / delta + 2; - else - hue = (r - g) / delta + 4; - hue *= 60; - if (hue < 0) - hue += 360; - } - - // Move saturation and lightness from 0-1 to 0-100 - saturation *= 100; - lightness *= 100; - - return { hue, saturation, lightness }; -} - -// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance -function relativeLuminance(hexCode: string) { - const normalize = (x: number) => - x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; - - const r = normalize(parseInt(hexCode.substring(0, 2), 16) / 255); - const g = normalize(parseInt(hexCode.substring(2, 4), 16) / 255); - const b = normalize(parseInt(hexCode.substring(4, 6), 16) / 255); - - return r * 0.2126 + g * 0.7152 + b * 0.0722; -} diff --git a/src/plugins/clientTheme/utils/colorUtils.ts b/src/plugins/clientTheme/utils/colorUtils.ts new file mode 100644 index 00000000..88d94714 --- /dev/null +++ b/src/plugins/clientTheme/utils/colorUtils.ts @@ -0,0 +1,65 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +// https://css-tricks.com/converting-color-spaces-in-javascript/ +export function hexToHSL(hexCode: string) { + // Hex => RGB normalized to 0-1 + const r = parseInt(hexCode.substring(0, 2), 16) / 255; + const g = parseInt(hexCode.substring(2, 4), 16) / 255; + const b = parseInt(hexCode.substring(4, 6), 16) / 255; + + // RGB => HSL + const cMax = Math.max(r, g, b); + const cMin = Math.min(r, g, b); + const delta = cMax - cMin; + + let hue: number; + let saturation: number; + let lightness: number; + + lightness = (cMax + cMin) / 2; + + if (delta === 0) { + // If r=g=b then the only thing that matters is lightness + hue = 0; + saturation = 0; + } else { + // Magic + saturation = delta / (1 - Math.abs(2 * lightness - 1)); + + if (cMax === r) { + hue = ((g - b) / delta) % 6; + } else if (cMax === g) { + hue = (b - r) / delta + 2; + } else { + hue = (r - g) / delta + 4; + } + + hue *= 60; + if (hue < 0) { + hue += 360; + } + } + + // Move saturation and lightness from 0-1 to 0-100 + saturation *= 100; + lightness *= 100; + + return { hue, saturation, lightness }; +} + +// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance +export function relativeLuminance(hexCode: string) { + const normalize = (x: number) => ( + x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4 + ); + + const r = normalize(parseInt(hexCode.substring(0, 2), 16) / 255); + const g = normalize(parseInt(hexCode.substring(2, 4), 16) / 255); + const b = normalize(parseInt(hexCode.substring(4, 6), 16) / 255); + + return r * 0.2126 + g * 0.7152 + b * 0.0722; +} diff --git a/src/plugins/clientTheme/utils/styleUtils.ts b/src/plugins/clientTheme/utils/styleUtils.ts new file mode 100644 index 00000000..bc6169d4 --- /dev/null +++ b/src/plugins/clientTheme/utils/styleUtils.ts @@ -0,0 +1,90 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { hexToHSL } from "./colorUtils"; + +const VARS_STYLE_ID = "vc-clientTheme-vars"; +const OVERRIDES_STYLE_ID = "vc-clientTheme-overrides"; + +export function createOrUpdateThemeColorVars(color: string) { + const { hue, saturation, lightness } = hexToHSL(color); + + createOrUpdateStyle(VARS_STYLE_ID, `:root { + --theme-h: ${hue}; + --theme-s: ${saturation}%; + --theme-l: ${lightness}%; + }`); +} + +export async function startClientTheme(color: string) { + createOrUpdateThemeColorVars(color); + createColorsOverrides(await getDiscordStyles()); +} + +export function disableClientTheme() { + document.getElementById(VARS_STYLE_ID)?.remove(); + document.getElementById(OVERRIDES_STYLE_ID)?.remove(); +} + +function getOrCreateStyle(styleId: string) { + const existingStyle = document.getElementById(styleId); + if (existingStyle) { + return existingStyle as HTMLStyleElement; + } + + const newStyle = document.createElement("style"); + newStyle.id = styleId; + + return document.head.appendChild(newStyle); +} + +function createOrUpdateStyle(styleId: string, css: string) { + const style = getOrCreateStyle(styleId); + style.textContent = css; +} + +/** + * @returns A string containing all the CSS styles from the Discord client. + */ +async function getDiscordStyles(): Promise { + const styleLinkNodes = document.querySelectorAll('link[rel="stylesheet"]'); + + const cssTexts = await Promise.all(Array.from(styleLinkNodes, async node => { + if (!node.href) + return null; + + return fetch(node.href).then(res => res.text()); + })); + + return cssTexts.filter(Boolean).join("\n"); +} + +const VISUAL_REFRESH_COLORS_VARIABLES_REGEX = /(--neutral-\d{1,3}?-hsl):.+?([\d.]+?)%;/g; + +function createColorsOverrides(styles: string) { + const visualRefreshColorsLightness = {} as Record; + + for (const [, colorVariableName, lightness] of styles.matchAll(VISUAL_REFRESH_COLORS_VARIABLES_REGEX)) { + visualRefreshColorsLightness[colorVariableName] = parseFloat(lightness); + } + + const lightThemeBaseLightness = visualRefreshColorsLightness["--neutral-2-hsl"]; + const darkThemeBaseLightness = visualRefreshColorsLightness["--neutral-69-hsl"]; + + createOrUpdateStyle(OVERRIDES_STYLE_ID, [ + `.visual-refresh.theme-light {\n ${generateNewColorVars(visualRefreshColorsLightness, lightThemeBaseLightness)} \n}`, + `.visual-refresh.theme-dark {\n ${generateNewColorVars(visualRefreshColorsLightness, darkThemeBaseLightness)} \n}`, + ].join("\n\n")); +} + +function generateNewColorVars(colorsLightess: Record, baseLightness: number) { + return Object.entries(colorsLightess).map(([colorVariableName, lightness]) => { + const lightnessOffset = lightness - baseLightness; + const plusOrMinus = lightnessOffset >= 0 ? "+" : "-"; + + return `${colorVariableName}: var(--theme-h) var(--theme-s) calc(var(--theme-l) ${plusOrMinus} ${Math.abs(lightnessOffset).toFixed(2)}%);`; + }).join("\n"); +} From 0a2d4a2ab2da38d2a1d91c838b1db31c392a870c Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 4 May 2025 16:25:13 -0300 Subject: [PATCH 113/198] Fix plugins for new V2 User Profile Modal --- src/plugins/friendsSince/index.tsx | 10 +++++++++- src/plugins/mutualGroupDMs/index.tsx | 29 ++++++++++++++++++++++++++-- src/plugins/mutualGroupDMs/style.css | 6 +++++- src/plugins/userVoiceShow/index.tsx | 2 +- src/plugins/viewIcons/index.tsx | 2 +- src/utils/modal.tsx | 2 +- 6 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index 07723bcc..b59c8a62 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -32,7 +32,15 @@ export default definePlugin({ }, // User Profile Modal { - find: "action:\"PRESS_APP_CONNECTION\"", + find: ".connections,userId:", + replacement: { + match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id),.{0,100}}\)}\),/, + replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:false})," + } + }, + // User Profile Modal v2 + { + find: ".MODAL_V2,onClose:", replacement: { match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id),.{0,100}}\)}\),/, replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:false})," diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 858a366c..94811ea7 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -87,6 +87,7 @@ export default definePlugin({ authors: [Devs.amia], patches: [ + // User Profile Modal { find: ".BOT_DATA_ACCESS?(", replacement: [ @@ -102,7 +103,31 @@ export default definePlugin({ // set the gap to zero to ensure ours stays on screen { match: /className:\i\.tabBar/, - replace: "$& + ' vc-mutual-gdms-tab-bar'" + replace: '$& + " vc-mutual-gdms-modal-tab-bar"' + } + ] + }, + // User Profile Modal v2 + { + find: ".tabBarPanel,children:", + replacement: [ + { + match: /items:(\i),.+?(?=return\(0,\i\.jsxs?\)\("div)/, + replace: "$&$self.pushSection($1,arguments[0].user);" + }, + { + match: /\.tabBarPanel,children:(?=.+?section:(\i))/, + replace: "$&$1==='MUTUAL_GDMS'?$self.renderMutualGDMs(arguments[0]):" + }, + // Make the gap between each item smaller so our tab can fit. + { + match: /className:\i\.tabBar/, + replace: '$& + " vc-mutual-gdms-modal-v2-tab-bar"' + }, + // Make the tab bar item text smaller so our tab can fit. + { + match: /(\.tabBarItem.+?variant:)"heading-lg\/medium"/, + replace: '$1"heading-md/medium"' } ] }, @@ -138,8 +163,8 @@ export default definePlugin({ sections[IS_PATCHED] = true; sections.push({ + text: getMutualGDMCountText(user), section: "MUTUAL_GDMS", - text: getMutualGDMCountText(user) }); } catch (e) { new Logger("MutualGroupDMs").error("Failed to push mutual group dms section:", e); diff --git a/src/plugins/mutualGroupDMs/style.css b/src/plugins/mutualGroupDMs/style.css index 3d06568f..698e21de 100644 --- a/src/plugins/mutualGroupDMs/style.css +++ b/src/plugins/mutualGroupDMs/style.css @@ -1,3 +1,7 @@ -.vc-mutual-gdms-tab-bar { +.vc-mutual-gdms-modal-tab-bar { gap: 0; } + +.vc-mutual-gdms-modal-v2-tab-bar { + gap: 6px; +} diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx index 3d119c43..0ee41414 100644 --- a/src/plugins/userVoiceShow/index.tsx +++ b/src/plugins/userVoiceShow/index.tsx @@ -55,7 +55,7 @@ export default definePlugin({ settings, patches: [ - // User Popout, Full Size Profile, Direct Messages Side Profile + // User Popout, User Profile Modal, Direct Messages Side Profile { find: "#{intl::USER_PROFILE_LOAD_ERROR}", replacement: { diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index afd9d48c..07630a00 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -190,7 +190,7 @@ export default definePlugin({ }, patches: [ - // Avatar component used in User DMs "User Profile" popup in the right and Profiles Modal pfp + // Avatar component used in User DMs "User Profile" popup in the right and User Profile Modal pfp { find: ".overlay:void 0,status:", replacement: [ diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index eebdb95e..17bf3987 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -140,7 +140,7 @@ export type MediaModalProps = { shouldHideMediaOptions?: boolean; }; -// modal key: "Media Viewer Modal" +// Modal key: "Media Viewer Modal" export const openMediaModal: (props: MediaModalProps) => void = findByCodeLazy("hasMediaOptions", "shouldHideMediaOptions"); interface ModalAPI { From 133e92442549308cc4b9fff728aa57a022216e1d Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 4 May 2025 17:05:48 -0300 Subject: [PATCH 114/198] BetterFolders: Add className to Sidebar component --- src/plugins/betterFolders/FolderSideBar.tsx | 10 ++++------ src/plugins/betterFolders/index.tsx | 2 +- .../betterFolders/{sidebarFix.css => style.css} | 5 ++++- 3 files changed, 9 insertions(+), 8 deletions(-) rename src/plugins/betterFolders/{sidebarFix.css => style.css} (88%) diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index cfd15517..40329122 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -43,15 +43,13 @@ export default ErrorBoundary.wrap(guildsBarProps => { // We need to display none if we are in fullscreen. Yes this seems horrible doing with css, but it's literally how Discord does it. // Also display flex otherwise to fix scrolling. - // gridArea is needed to align properly with the base app grid. - const barStyle = { - display: isFullscreen ? "none" : "flex", - gridArea: "betterFoldersSidebar" + const sidebarStyle = { + display: isFullscreen ? "none" : "flex" } satisfies CSSProperties; if (!guilds || !settings.store.sidebarAnim) { return visible - ?
{Sidebar}
+ ?
{Sidebar}
: null; } @@ -65,7 +63,7 @@ export default ErrorBoundary.wrap(guildsBarProps => { > {(animationStyle: any, show: any) => show && ( - + {Sidebar} ) diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index ca46f843..3dc812a4 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import "./sidebarFix.css"; +import "./style.css"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; diff --git a/src/plugins/betterFolders/sidebarFix.css b/src/plugins/betterFolders/style.css similarity index 88% rename from src/plugins/betterFolders/sidebarFix.css rename to src/plugins/betterFolders/style.css index b7c0e66e..a3c82dcb 100644 --- a/src/plugins/betterFolders/sidebarFix.css +++ b/src/plugins/betterFolders/style.css @@ -1,5 +1,8 @@ -/* These area names need to be hardcoded. Only betterFoldersSidebar is added by the plugin. */ +.vc-betterFolders-sidebar { + grid-area: betterFoldersSidebar +} +/* These area names need to be hardcoded. Only betterFoldersSidebar is added by the plugin. */ .visual-refresh .vc-betterFolders-sidebar-grid { /* stylelint-disable-next-line value-keyword-case */ grid-template-columns: [start] min-content [guildsEnd] min-content [sidebarEnd] min-content [channelsEnd] 1fr [end]; From f7200608b5674b5a6d845f2330b468f83c3892a1 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 4 May 2025 20:21:13 -0400 Subject: [PATCH 115/198] Revert Some Changes On MessageTags --- src/plugins/messageTags/index.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/plugins/messageTags/index.ts b/src/plugins/messageTags/index.ts index 2fb03bf7..e1fd9506 100644 --- a/src/plugins/messageTags/index.ts +++ b/src/plugins/messageTags/index.ts @@ -25,10 +25,6 @@ import definePlugin, { OptionType } from "@utils/types"; const EMOTE = "<:luna:1035316192220553236>"; const DATA_KEY = "MessageTags_TAGS"; const MessageTagsMarker = Symbol("MessageTags"); -const author = { - id: "821472922140803112", - bot: false -}; interface Tag { name: string; @@ -59,7 +55,6 @@ function createTagCommand(tag: Tag) { execute: async (_, ctx) => { if (!getTag(tag.name)) { sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} The tag **${tag.name}** does not exist anymore! Please reload ur Discord to fix :)` }); return { content: `/${tag.name}` }; @@ -76,6 +71,7 @@ function createTagCommand(tag: Tag) { const settings = definePluginSettings({ clyde: { + name: "Clyde message on send", description: "If enabled, clyde will send you an ephemeral message when a tag was used.", type: OptionType.BOOLEAN, default: true @@ -83,7 +79,7 @@ const settings = definePluginSettings({ tagsList: { type: OptionType.CUSTOM, default: {} as Record, - description: "", + description: "" } }); @@ -176,7 +172,6 @@ export default definePlugin({ if (getTag(name)) return sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} A Tag with the name **${name}** already exists!` }); @@ -189,7 +184,6 @@ export default definePlugin({ addTag(tag); sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} Successfully created the tag **${name}**!` }); break; // end 'create' @@ -199,7 +193,6 @@ export default definePlugin({ if (!getTag(name)) return sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} A Tag with the name **${name}** does not exist!` }); @@ -207,14 +200,12 @@ export default definePlugin({ removeTag(name); sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} Successfully deleted the tag **${name}**!` }); break; // end 'delete' } case "list": { sendBotMessage(ctx.channel.id, { - author, embeds: [ { title: "All Tags:", @@ -235,12 +226,10 @@ export default definePlugin({ if (!tag) return sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} A Tag with the name **${name}** does not exist!` }); sendBotMessage(ctx.channel.id, { - author, content: tag.message.replaceAll("\\n", "\n") }); break; // end 'preview' @@ -248,7 +237,6 @@ export default definePlugin({ default: { sendBotMessage(ctx.channel.id, { - author, content: "Invalid sub-command" }); break; From 73f68fe0b776e1e5738930478ac60bc1e01a1ae6 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 4 May 2025 22:03:42 -0300 Subject: [PATCH 116/198] MutualGroupDMs: Fix overlap in V2 User Profile Modal --- src/plugins/mutualGroupDMs/index.tsx | 2 +- src/plugins/mutualGroupDMs/style.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 94811ea7..d88e1ee0 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -127,7 +127,7 @@ export default definePlugin({ // Make the tab bar item text smaller so our tab can fit. { match: /(\.tabBarItem.+?variant:)"heading-lg\/medium"/, - replace: '$1"heading-md/medium"' + replace: '$1"heading-sm/medium"' } ] }, diff --git a/src/plugins/mutualGroupDMs/style.css b/src/plugins/mutualGroupDMs/style.css index 698e21de..f0ad3c60 100644 --- a/src/plugins/mutualGroupDMs/style.css +++ b/src/plugins/mutualGroupDMs/style.css @@ -3,5 +3,5 @@ } .vc-mutual-gdms-modal-v2-tab-bar { - gap: 6px; + gap: 12px; } From 9a3c66abfda58cf868f5741f9954782dd74d98d5 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 5 May 2025 03:42:38 +0200 Subject: [PATCH 117/198] Remove NSFWGateBypass This plugin was always meant as a tool for adults who don't want to give Discord their birthday/ID. But obviously there's nothing stopping minors from using this which is troublesome legally and possibly could lead to me being legally liable for distributing it. Bypassing it is also becoming progressively harder with the changes Discord is making to abide with UK and Australia law. As such, I no longer feel comfortable providing this plugin. If you rely on this plugin (as an adult), find a suitable third party replacement from elsewhere. --- src/plugins/nsfwGateBypass/index.ts | 41 ----------------------------- 1 file changed, 41 deletions(-) delete mode 100644 src/plugins/nsfwGateBypass/index.ts diff --git a/src/plugins/nsfwGateBypass/index.ts b/src/plugins/nsfwGateBypass/index.ts deleted file mode 100644 index 6d0cb702..00000000 --- a/src/plugins/nsfwGateBypass/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2025 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 . -*/ - -import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; - -export default definePlugin({ - name: "NSFWGateBypass", - description: "Allows you to access NSFW channels without setting/verifying your age", - authors: [Devs.Commandtechno], - patches: [ - { - find: ".nsfwAllowed=null", - replacement: [ - { - match: /(?<=\.nsfwAllowed=)null!=.+?(?=[,;])/, - replace: "true", - }, - { - match: /(?<=\.ageVerificationStatus=)null!=.+?(?=[,;])/, - replace: "3", // VERIFIED_ADULT - } - ], - } - ], -}); From 238482a0b760d4b6e8188c190ed4fd9abfa58d6b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 4 May 2025 22:16:50 -0400 Subject: [PATCH 118/198] UserVoiceShow: Remove Readded Section --- src/plugins/userVoiceShow/index.tsx | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx index 31417c0e..5f1a05e5 100644 --- a/src/plugins/userVoiceShow/index.tsx +++ b/src/plugins/userVoiceShow/index.tsx @@ -56,34 +56,6 @@ export default definePlugin({ settings, patches: [ - // User Popout, User Profile Modal, Direct Messages Side Profile - { - find: "#{intl::USER_PROFILE_LOAD_ERROR}", - replacement: { - match: /(\.fetchError.+?\?)null/, - replace: (_, rest) => `${rest}$self.VoiceChannelIndicator({userId:arguments[0]?.userId})` - }, - predicate: () => settings.store.showInUserProfileModal - }, - // To use without the MemberList decorator API - /* // Guild Members List - { - find: ".lostPermission)", - replacement: { - match: /\.lostPermission\).+?(?=avatar:)/, - replace: "$&children:[$self.VoiceChannelIndicator({userId:arguments[0]?.user?.id})]," - }, - predicate: () => settings.store.showVoiceChannelIndicator - }, - // Direct Messages List - { - find: "PrivateChannel.renderAvatar", - replacement: { - match: /#{intl::CLOSE_DM}.+?}\)(?=])/, - replace: "$&,$self.VoiceChannelIndicator({userId:arguments[0]?.user?.id})" - }, - predicate: () => settings.store.showVoiceChannelIndicator - }, */ // Friends List { find: "null!=this.peopleListItemRef.current", From 281e9caeff0ae7a332844fab2a3cd15001e76cbc Mon Sep 17 00:00:00 2001 From: Eazvy <57739965+Eazvy@users.noreply.github.com> Date: Sun, 4 May 2025 23:40:40 -0400 Subject: [PATCH 119/198] add (autoJump) (#245) * Create index.tsx * Update index.tsx * Create index.tsx * Update index.tsx * Update index.tsx * Create index.tsx * Delete index.tsx * Delete index.tsx * Create index.tsx * update plugin-settings / main settings yep * Delete index.tsx * Revert * Add AutoJump * Only Trigger Flux On Setting * Remove Extra Auto * Misc Fix --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> --- src/components/PluginSettings/styles.css | 8 +-- src/equicordplugins/autoJump/index.tsx | 69 +++++++++++++++++++++ src/equicordplugins/purgeMessages/index.tsx | 5 +- 3 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 src/equicordplugins/autoJump/index.tsx diff --git a/src/components/PluginSettings/styles.css b/src/components/PluginSettings/styles.css index ce131729..488fd212 100644 --- a/src/components/PluginSettings/styles.css +++ b/src/components/PluginSettings/styles.css @@ -252,7 +252,7 @@ } .visual-refresh .button-danger-background:hover { - background-color: var(--status-danger-background) !important; + background-color: var(--status-danger-background) !important; color: var(--status-danger-text) !important; } @@ -261,15 +261,15 @@ border: 1px solid var(--border-subtle) !important; &:hover { - background-color: var(--card-primary-bg) !important; + background-color: var(--card-primary-bg) !important; } - } +} .visual-refresh .vc-plugin-stats { background-color: var(--card-primary-bg) !important; border: 1px solid var(--border-subtle) !important; &:hover { - background-color: var(--card-primary-bg) !important; + background-color: var(--card-primary-bg) !important; } } diff --git a/src/equicordplugins/autoJump/index.tsx b/src/equicordplugins/autoJump/index.tsx new file mode 100644 index 00000000..b05ba1e6 --- /dev/null +++ b/src/equicordplugins/autoJump/index.tsx @@ -0,0 +1,69 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { definePluginSettings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { ChannelStore, Menu, MessageActions, NavigationRouter } from "@webpack/common"; + +interface ChannelSelectEvent { + type: "CHANNEL_SELECT"; + channelId: string | null; + guildId: string | null; +} + +let lastChannelId = "0"; + +function autoJump({ guild_id, id: channelId }) { + const guildId = guild_id ?? "@me"; + + lastChannelId = channelId; + NavigationRouter.transitionTo(`/channels/${guildId}/${channelId}`); + MessageActions.jumpToPresent(channelId, { limit: null }); +} + +const MenuPatch: NavContextMenuPatchCallback = (children, { channel }) => { + children.push( + { + autoJump(channel); + }} + /> + ); +}; + +const settings = definePluginSettings({ + autoJumping: { + type: OptionType.BOOLEAN, + description: "Automatically jump to the last message in the channel when switching channels", + default: false + } +}); + +export default definePlugin({ + name: "AutoJump", + description: "Jumps to Last Message in Channel", + authors: [EquicordDevs.omaw], + settings, + contextMenus: { + "channel-context": MenuPatch, + "user-context": MenuPatch, + "thread-context": MenuPatch + }, + flux: { + async CHANNEL_SELECT({ guildId, channelId }: ChannelSelectEvent) { + if (!settings.store.autoJumping || !channelId) return; + + const channel = ChannelStore.getChannel(channelId); + if (!channel || channel.id === lastChannelId) return; + + autoJump({ guild_id: guildId, id: channelId }); + } + } +}); diff --git a/src/equicordplugins/purgeMessages/index.tsx b/src/equicordplugins/purgeMessages/index.tsx index 425754fb..03269bf8 100644 --- a/src/equicordplugins/purgeMessages/index.tsx +++ b/src/equicordplugins/purgeMessages/index.tsx @@ -21,12 +21,9 @@ import "@equicordplugins/_misc/styles.css"; import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; -import { Forms, MessageStore, UserStore } from "@webpack/common"; +import { Forms, MessageActions, MessageStore, UserStore } from "@webpack/common"; import { Channel, Message } from "discord-types/general"; -const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage"); - async function deleteMessages(amount: number, channel: Channel, delay: number = 1500): Promise { let deleted = 0; const userId = UserStore.getCurrentUser().id; From c989a9c7bd5bd7d63540eafc76a7066d27c53698 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 4 May 2025 23:47:57 -0400 Subject: [PATCH 120/198] SpotifyActivityToggle: Unhide Setting --- .../spotifyActivityToggle/index.tsx | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/equicordplugins/spotifyActivityToggle/index.tsx b/src/equicordplugins/spotifyActivityToggle/index.tsx index f20ac4dc..a9f3b283 100644 --- a/src/equicordplugins/spotifyActivityToggle/index.tsx +++ b/src/equicordplugins/spotifyActivityToggle/index.tsx @@ -35,9 +35,24 @@ function makeSpotifyIcon(enabled: boolean) { ); } +async function setSpotifyActivity(value: boolean) { + const { body } = await RestAPI.get({ + url: Constants.Endpoints.CONNECTIONS + }); + + if (!body) return; + const spotifyId = body.find(conn => conn.type === "spotify")?.id; + + await RestAPI.patch({ + url: Constants.Endpoints.CONNECTION("spotify", spotifyId), + body: { + show_activity: value, + }, + }); +} + function SpotifyActivityToggleButton() { const { spotifyConnection } = settings.store; - const setSpotifyConnection = value => settings.store.spotifyConnection = value; return (