From 235e7d4138e02c4428f38e08d7247cecfc131ea6 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 3 Mar 2026 19:11:05 -0500 Subject: [PATCH 01/29] Fix Searchable Selects --- src/components/settings/tabs/sync/CloudTab.tsx | 17 +++++++++-------- src/equicordplugins/exitSounds/index.tsx | 2 +- .../instantScreenshare/utils.tsx | 2 +- src/equicordplugins/timezones/TimezoneModal.tsx | 2 +- src/equicordplugins/vcNarratorCustom/index.tsx | 2 +- .../voiceMessageTranscriber.desktop/index.tsx | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/components/settings/tabs/sync/CloudTab.tsx b/src/components/settings/tabs/sync/CloudTab.tsx index 4c660cb3a..e848106c8 100644 --- a/src/components/settings/tabs/sync/CloudTab.tsx +++ b/src/components/settings/tabs/sync/CloudTab.tsx @@ -123,14 +123,15 @@ function CloudTab() { Choose which cloud backend to use for storing your settings. You can switch between Equicord's and Vencord's cloud services, or use a self-hosted instance. - o.value === cloud.url)} - onChange={v => changeUrl(v)} - className={Margins.bottom16} - closeOnSelect={true} - renderOptionPrefix={o => o?.value?.includes("equicord") ? : } - /> +
+ o.value === cloud.url)?.value} + onChange={v => changeUrl(v)} + closeOnSelect={true} + renderOptionPrefix={o => o?.value?.includes("equicord") ? : } + /> +
diff --git a/src/equicordplugins/exitSounds/index.tsx b/src/equicordplugins/exitSounds/index.tsx index 3f161fbb6..db67fe6ed 100644 --- a/src/equicordplugins/exitSounds/index.tsx +++ b/src/equicordplugins/exitSounds/index.tsx @@ -30,7 +30,7 @@ function GuildSelector() { return ( o.value === soundGuildId)} + value={options.find(o => o.value === soundGuildId)?.value} placeholder="Select a server..." maxVisibleItems={6} closeOnSelect={true} diff --git a/src/equicordplugins/instantScreenshare/utils.tsx b/src/equicordplugins/instantScreenshare/utils.tsx index b6dbbece8..22ac85cea 100644 --- a/src/equicordplugins/instantScreenshare/utils.tsx +++ b/src/equicordplugins/instantScreenshare/utils.tsx @@ -105,7 +105,7 @@ function StreamSimplePicker({ streamMediaSelection, streamMedia }: PickerProps) placeholder="Select a media source to stream " maxVisibleItems={5} options={options} - value={options.find(o => o.value === streamMedia)} + value={options.find(o => o.value === streamMedia)?.value} onChange={v => settings.store.streamMedia = v} closeOnSelect /> diff --git a/src/equicordplugins/timezones/TimezoneModal.tsx b/src/equicordplugins/timezones/TimezoneModal.tsx index 8d599fd1f..e6c7116ff 100644 --- a/src/equicordplugins/timezones/TimezoneModal.tsx +++ b/src/equicordplugins/timezones/TimezoneModal.tsx @@ -56,7 +56,7 @@ export function SetTimezoneModal({ userId, modalProps, database }: { userId: str o.value === currentValue)} + value={options.find(o => o.value === currentValue)?.value} placeholder={"Select a Timezone"} maxVisibleItems={5} closeOnSelect={true} diff --git a/src/equicordplugins/vcNarratorCustom/index.tsx b/src/equicordplugins/vcNarratorCustom/index.tsx index e1a340bf3..f4711d878 100644 --- a/src/equicordplugins/vcNarratorCustom/index.tsx +++ b/src/equicordplugins/vcNarratorCustom/index.tsx @@ -794,7 +794,7 @@ function VoiceSelectModal({ modalProps, user }: { modalProps: ModalProps; user: Select voice for {user.username} o.value === currentValue)} + value={options.find(o => o.value === currentValue)?.value} placeholder="Select a voice" maxVisibleItems={6} closeOnSelect={true} diff --git a/src/equicordplugins/voiceMessageTranscriber.desktop/index.tsx b/src/equicordplugins/voiceMessageTranscriber.desktop/index.tsx index 608d08f50..f60724321 100644 --- a/src/equicordplugins/voiceMessageTranscriber.desktop/index.tsx +++ b/src/equicordplugins/voiceMessageTranscriber.desktop/index.tsx @@ -132,7 +132,7 @@ function LanguageSelectionModal(props: { modalProps: ModalProps, src: string; }) o.value === language)} + value={languageOptions.find(o => o.value === language)?.value} onChange={setLanguage} />
From b6e779822ada5c1fe844dc8784cde92c11a7d86b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 3 Mar 2026 19:34:20 -0500 Subject: [PATCH 02/29] mainWindowFrameless: no idea if this works --- src/api/Settings.ts | 2 ++ src/components/settings/tabs/vencord/index.tsx | 12 ++++++++++-- src/equicordplugins/messagePeek/index.tsx | 2 +- src/main/patcher.ts | 5 ++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/api/Settings.ts b/src/api/Settings.ts index 208c37698..f0db98933 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -49,6 +49,7 @@ export interface Settings { themeNames: Record; enableReactDevtools: boolean; themeLinks: string[]; + mainWindowFrameless: boolean; frameless: boolean; transparent: boolean; winCtrlQ: boolean; @@ -116,6 +117,7 @@ const DefaultSettings: Settings = { pinnedThemes: [], themeNames: {}, enableReactDevtools: false, + mainWindowFrameless: false, frameless: false, transparent: false, winCtrlQ: false, diff --git a/src/components/settings/tabs/vencord/index.tsx b/src/components/settings/tabs/vencord/index.tsx index ee1d5eadf..37901be6d 100644 --- a/src/components/settings/tabs/vencord/index.tsx +++ b/src/components/settings/tabs/vencord/index.tsx @@ -81,11 +81,18 @@ function EquicordSettings() { restartRequired: true, warning: { enabled: false }, }, + (!IS_WEB && !IS_DISCORD_DESKTOP || !IS_WINDOWS) && { + key: "mainWindowFrameless", + title: "Disable the Main Window Frame", + description: "Remove the native window frame for a cleaner look. You can still move the window by dragging the title bar area.", + restartRequired: true, + warning: { enabled: false }, + }, !IS_WEB && (!IS_DISCORD_DESKTOP || !IS_WINDOWS ? { key: "frameless", - title: "Disable the Window Frame", + title: "Disable All Window Frames", description: "Remove the native window frame for a cleaner look. You can still move the window by dragging the title bar area.", restartRequired: true, warning: { enabled: false }, @@ -96,7 +103,8 @@ function EquicordSettings() { description: "Replace Discord's custom title bar with the standard Windows title bar. This may improve compatibility with some window management tools.", restartRequired: true, warning: { enabled: false }, - }), + } + ), !IS_WEB && { key: "transparent", title: "Enable Window Transparency", diff --git a/src/equicordplugins/messagePeek/index.tsx b/src/equicordplugins/messagePeek/index.tsx index 8d07c8492..866e704e9 100644 --- a/src/equicordplugins/messagePeek/index.tsx +++ b/src/equicordplugins/messagePeek/index.tsx @@ -224,7 +224,7 @@ function Timestamp({ channel }: { channel: Channel; }) { if (!lastMessage) return null; const timestamp = SnowflakeUtils.extractTimestamp(lastMessage.id); - const className = ExperimentStore.getUserExperimentBucket("2026-01-favorites-server") >= 1 ? cl("timestamp-favorites") : cl("timestamp"); + const className = ExperimentStore.getUserExperimentBucket("2026-01-favorites-server") > 0 ? cl("timestamp-favorites") : cl("timestamp"); return {formatRelativeTime(timestamp)}; } diff --git a/src/main/patcher.ts b/src/main/patcher.ts index 60cc515f4..c5584f300 100644 --- a/src/main/patcher.ts +++ b/src/main/patcher.ts @@ -72,12 +72,15 @@ if (!IS_VANILLA) { constructor(options: BrowserWindowConstructorOptions) { if (options?.webPreferences?.preload && options.title) { const original = options.webPreferences.preload; + const isMainWindow = options.title === "Discord"; options.webPreferences.preload = join(__dirname, "preload.js"); options.webPreferences.sandbox = false; // work around discord unloading when in background options.webPreferences.backgroundThrottling = false; - if (settings.frameless) { + if (settings.mainWindowFrameless && isMainWindow) { + options.frame = false; + } else if (settings.frameless) { options.frame = false; } else if (process.platform === "win32" && settings.winNativeTitleBar) { delete options.frame; From d613793dc4446f34ee1e9be4a844f9ea0db32add Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 3 Mar 2026 19:36:05 -0500 Subject: [PATCH 03/29] do frameless first --- src/main/patcher.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/patcher.ts b/src/main/patcher.ts index c5584f300..911a40473 100644 --- a/src/main/patcher.ts +++ b/src/main/patcher.ts @@ -78,9 +78,9 @@ if (!IS_VANILLA) { // work around discord unloading when in background options.webPreferences.backgroundThrottling = false; - if (settings.mainWindowFrameless && isMainWindow) { + if (settings.frameless) { options.frame = false; - } else if (settings.frameless) { + } else if (settings.mainWindowFrameless && isMainWindow) { options.frame = false; } else if (process.platform === "win32" && settings.winNativeTitleBar) { delete options.frame; From 2ae503d9305ad7bb211ae7b79dc988552f5842be Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:38:40 -0500 Subject: [PATCH 04/29] BetterSettings: Fix For New Menu --- src/plugins/_core/settings.tsx | 8 --- src/plugins/betterSettings/index.tsx | 91 ++++++++-------------------- 2 files changed, 24 insertions(+), 75 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index d5a57d933..eb87cb137 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -152,14 +152,6 @@ export default definePlugin({ } ] }, - { - find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}", - replacement: { - // Skip the check Discord performs to make sure the section being selected in the user settings context menu is valid - match: /null!=\(\i=Object.values\(\i\.\i\).{0,50}?&&(?=\(0,\i\.openUserSettings\)\(\i,\{section:\i)/, - replace: "" - } - }, { find: ".buildLayout().map", replacement: { diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index 7b0b3517e..1f2578b94 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -7,14 +7,13 @@ import { definePluginSettings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import { buildPluginMenuEntries, buildThemeMenuEntries } from "@equicordplugins/equicordToolbox/menu"; -import SettingsPlugin from "@plugins/_core/settings"; import { Devs } from "@utils/constants"; import { classNameFactory } from "@utils/css"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; import { findCssClassesLazy } from "@webpack"; import { ComponentDispatch, FocusLock, Menu, useEffect, useRef } from "@webpack/common"; -import type { HTMLAttributes, ReactElement } from "react"; +import type { HTMLAttributes } from "react"; import fullHeightStyle from "./fullHeightContext.css?managed"; @@ -171,13 +170,9 @@ export default definePlugin({ find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}", replacement: [ { - match: /=\[\];(\i)(?=\.forEach.{0,200}?"logout"===\i.{0,100}?(\i)\.get\(\i\))/, - replace: "=$self.wrapMap([]);$self.transformSettingsEntries($1,$2)", + match: /(\i)=(.{0,20}openUserSettings\)\(\i\)\));/, + replace: "$1=$self.transformSettingsElements($2);", }, - { - match: /case \i\.\i\.DEVELOPER_OPTIONS:return \i;/, - replace: "$&case 'EquicordPlugins':return $self.buildPluginMenuEntries(true);case 'EquicordThemes':return $self.buildThemeMenuEntries();" - } ] }, ], @@ -200,72 +195,34 @@ export default definePlugin({ return ; }, - transformSettingsEntries(list: SettingsEntry[], keyMap: Map) { + transformSettingsElements(list) { const items: any[] = []; - const equicordItems: SettingsEntry[] = [ - { section: "EquicordSettings", label: "Settings" }, - { section: "EquicordPlugins", label: "Plugins" }, - { section: "EquicordThemes", label: "Themes" }, - { section: "EquicordUpdater", label: "Updater" }, - { section: "EquicordChangelog", label: "Changelog" }, - { section: "EquicordCloud", label: "Cloud" }, - { section: "EquicordBackupAndRestore", label: "Backup & Restore" }, - { section: "EquicordPatchHelper", label: "Patch Helper" }, - ]; - for (const [section, key] of SettingsPlugin.settingsSectionMap) { - const entry = SettingsPlugin.customEntries.find(e => key.endsWith(e.key)); - if (entry) equicordItems.push({ section, label: entry.title }); - } + for (const item of list) { + const { key, props } = item; + if (!props) continue; - if (settings.store.organizeMenu) { - for (const item of list) { - if (!item.label || item.predicate != null && !item.predicate()) continue; + if (key === "equicord_plugins" || key === "equicord_themes") { + const children = key === "equicord_plugins" + ? buildPluginMenuEntries() + : buildThemeMenuEntries(); - if (item.section === "HEADER") { - keyMap.set(item.label, item.label); - items.push({ section: item.label, items: [] }); - } else if (item.section !== "DIVIDER" && keyMap.has(item.section)) { - items.at(-1)?.items.push(item); - } + items.push( + + {children} + + ); + } else if (key.endsWith("_section") && props.label) { + items.push( + + {this.transformSettingsElements(props.children)} + + ); + } else { + items.push(item); } - - keyMap.set("Equicord", "Equicord"); - items.push({ - section: "Equicord", - items: equicordItems - }); - } else { - items.push(...list, ...equicordItems); } return items; - }, - - wrapMap(toWrap: TransformedSettingsEntry[]) { - if (!settings.store.organizeMenu) return toWrap; - // @ts-expect-error - toWrap.map = function (render: (item: SettingsEntry) => ReactElement) { - return this - .filter(a => a.items.length > 0) - .map(({ section, items }) => { - const children = items.map(render); - if (section) { - return ( - - {children} - - ); - } else { - return children; - } - }); - }; - - return toWrap; } }); From 1c63caa0c5f0d3c5ebf47caa11393003b33661e2 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:27:19 -0500 Subject: [PATCH 05/29] MoreCommands: Make Freaky Default --- src/equicordplugins/moreCommands/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/moreCommands/utils.ts b/src/equicordplugins/moreCommands/utils.ts index 38a53a517..a5a0f7815 100644 --- a/src/equicordplugins/moreCommands/utils.ts +++ b/src/equicordplugins/moreCommands/utils.ts @@ -11,7 +11,7 @@ export const settings = definePluginSettings({ addFreakyEnding: { type: OptionType.BOOLEAN, description: "Add ๐Ÿ‘… or โค๏ธ at the end", - default: true + default: false }, uwuEveryMessage: { description: "Make every single message uwuified", From 753c29e61f3ddf13923c631f42829559c12a03aa Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:42:13 -0500 Subject: [PATCH 06/29] Fix NicknamesIconsAPI --- src/plugins/_api/nicknameIcons.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/plugins/_api/nicknameIcons.ts b/src/plugins/_api/nicknameIcons.ts index 81423e450..6d0c220b6 100644 --- a/src/plugins/_api/nicknameIcons.ts +++ b/src/plugins/_api/nicknameIcons.ts @@ -14,10 +14,16 @@ export default definePlugin({ patches: [ { find: "#{intl::USER_PROFILE_PRONOUNS}", - replacement: { - match: /(?<=shouldUnderlineOnHover:null.{0,200})children:(\i)/, - replace: "children:[...Vencord.Api.NicknameIcons._renderIcons({userId:arguments[0].user?.id}),$1]" - } + replacement: [ + { + match: /(?<=,\i\]\}\)\),)null!=\i/, + replace: "($&||Vencord.Api.NicknameIcons._renderIcons({userId:arguments[0].user?.id})?.length)" + }, + { + match: /(?<=shouldUnderlineOnHover:null.{0,300})children:(\i)/, + replace: "children:[...Vencord.Api.NicknameIcons._renderIcons({userId:arguments[0].user?.id}),$1]" + } + ] } ] }); From 1058bb12e97f22acc4126c19ca48d6fe4a711c67 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:52:19 -0500 Subject: [PATCH 07/29] AlwaysAnimate: Hopefully speed up nameplate patch --- src/plugins/alwaysAnimate/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/alwaysAnimate/index.ts b/src/plugins/alwaysAnimate/index.ts index 2052998ad..3c26219de 100644 --- a/src/plugins/alwaysAnimate/index.ts +++ b/src/plugins/alwaysAnimate/index.ts @@ -92,7 +92,7 @@ export default definePlugin({ find: ".MINI_PREVIEW,[", predicate: () => settings.store.nameplates, replacement: { - match: /animate:\i,loop:.{0,15}===\i/, + match: /animate:\i,loop:(?:!0===\i(?:&&)?){2}/, replace: "animate:true,loop:true" }, }, From 00d797b77c4d43847858cbdf202e8c8e4e06e6d3 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:02:19 -0500 Subject: [PATCH 08/29] AlwaysAnimate: Update Nameplate Patch --- src/plugins/alwaysAnimate/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/alwaysAnimate/index.ts b/src/plugins/alwaysAnimate/index.ts index 3c26219de..48d1335aa 100644 --- a/src/plugins/alwaysAnimate/index.ts +++ b/src/plugins/alwaysAnimate/index.ts @@ -92,7 +92,7 @@ export default definePlugin({ find: ".MINI_PREVIEW,[", predicate: () => settings.store.nameplates, replacement: { - match: /animate:\i,loop:(?:!0===\i(?:&&)?){2}/, + match: /animate:\i,loop:!0===\i&&!0===\i/, replace: "animate:true,loop:true" }, }, From a1a12eee41889e152eba1f023dfc43ad1328abcf Mon Sep 17 00:00:00 2001 From: nexpid Date: Wed, 4 Mar 2026 16:04:23 +0100 Subject: [PATCH 09/29] feat(SongSpotlight): support refresh tokens (#870) --- .../songSpotlight.desktop/lib/api.ts | 66 +++++++++++-------- .../songSpotlight.desktop/lib/oauth2.tsx | 14 +++- .../lib/stores/AuthorizationStore.ts | 39 ++++++++--- .../songSpotlight.desktop/settings.tsx | 5 +- .../ui/settings/index.tsx | 6 +- .../ui/songs/CollapsedProfileSongs.tsx | 7 +- 6 files changed, 91 insertions(+), 46 deletions(-) diff --git a/src/equicordplugins/songSpotlight.desktop/lib/api.ts b/src/equicordplugins/songSpotlight.desktop/lib/api.ts index 868f79132..9bad09bca 100644 --- a/src/equicordplugins/songSpotlight.desktop/lib/api.ts +++ b/src/equicordplugins/songSpotlight.desktop/lib/api.ts @@ -9,7 +9,6 @@ import { showToast, Toasts, UserStore } from "@webpack/common"; import { useAuthorizationStore } from "./stores/AuthorizationStore"; import { useSongStore } from "./stores/SongStore"; -import { logger } from "./utils"; export const apiConstants = { api: "https://dc.songspotlight.nexpid.xyz/", @@ -20,16 +19,36 @@ export const apiConstants = { songLimit: 6, }; -export async function authFetch(_url: string | URL, options?: RequestInit) { - const url = new URL(_url); +let refreshPromise: Promise | undefined; +async function refreshAccessToken() { + const token = useAuthorizationStore.getState().getToken(); + if (!token) return false; + return refreshPromise ??= fetch(new URL("api/auth/refresh", apiConstants.api), { + method: "POST", + headers: { + "X-Refresh-Token": token.refresh, + }, + body: token.access, + }).then(async res => { + if (!res.ok) return false; + + const access = await res.text(); + useAuthorizationStore.getState().setToken(access, token.refresh); + return true; + }).finally(() => refreshPromise = undefined); +} + +export async function authFetch(url: string | URL, options?: RequestInit, retried = false) { + url = new URL(url); try { + const token = useAuthorizationStore.getState().getToken(); const res = await fetch(url, { ...options, headers: { ...options?.headers, - Authorization: useAuthorizationStore.getState().getToken()!, - }, + Authorization: token?.access, + } as HeadersInit, }); if (res.ok) return res; @@ -37,36 +56,29 @@ export async function authFetch(_url: string | URL, options?: RequestInit) { // not modified if (res.status === 304) return null; + const text = await res.text(); + // unauthorized if (res.status === 401) { - useAuthorizationStore.getState().deleteToken(); + const retry = !retried && await refreshAccessToken(); + if (retry) return await authFetch(url, options, true); + else { + useAuthorizationStore.getState().deleteTokens(); + showToast("You have been signed out from Song Spotlight. Please sign in again.", Toasts.Type.FAILURE); + } + } else { + showToast( + !text.includes("") && res.status >= 400 && res.status <= 599 + ? `Song Spotlight: ${text}` + : `Song Spotlight fetch error at ${url.pathname}`, + Toasts.Type.FAILURE, + ); } - const text = await res.text(); - showToast( - !text.includes("") && res.status >= 400 && res.status <= 599 - ? `Song Spotlight: ${text}` - : `Song Spotlight fetch error at ${url.pathname}`, - Toasts.Type.FAILURE, - ); - - logger.error( - "Got an authFetch response error", - options?.method ?? "GET", - url.toString(), - res.status, - text, - ); throw new Error(text); } catch (error) { showToast(`Song Spotlight: ${error}`, Toasts.Type.FAILURE); - logger.error( - "Got an authFetch request error", - options?.method ?? "GET", - url.toString(), - error, - ); throw error; } } diff --git a/src/equicordplugins/songSpotlight.desktop/lib/oauth2.tsx b/src/equicordplugins/songSpotlight.desktop/lib/oauth2.tsx index 3ff3bcdf9..8780a8df0 100644 --- a/src/equicordplugins/songSpotlight.desktop/lib/oauth2.tsx +++ b/src/equicordplugins/songSpotlight.desktop/lib/oauth2.tsx @@ -30,15 +30,23 @@ export function presentOAuth2Modal() { const url = new URL(location); url.searchParams.append("whois", "equicord"); - const token = await (await authFetch(url))?.text(); - if (!token) return; + const res = await authFetch(url); + if (!res) throw "Response wasn't ok"; - useAuthorizationStore.getState().setToken(token); + const access = await res.text(); + if (!access) throw "Access token is missing"; + + const refresh = res.headers.get("X-Refresh-Token"); + // STUB uncomment this once API is fully rolled out + // if (!refresh) throw "Refresh token is missing"; + + useAuthorizationStore.getState().setToken(access, refresh || ""); getData(); showToast("Successfully authorized!", Toasts.Type.SUCCESS); } catch (error) { logger.error("Got an error during OAuth2", error); + if (typeof error === "string") showToast(error, Toasts.Type.FAILURE); } }} /> diff --git a/src/equicordplugins/songSpotlight.desktop/lib/stores/AuthorizationStore.ts b/src/equicordplugins/songSpotlight.desktop/lib/stores/AuthorizationStore.ts index 48e1469d5..368166764 100644 --- a/src/equicordplugins/songSpotlight.desktop/lib/stores/AuthorizationStore.ts +++ b/src/equicordplugins/songSpotlight.desktop/lib/stores/AuthorizationStore.ts @@ -10,11 +10,16 @@ import { UserStore, zustandCreate, zustandPersist } from "@webpack/common"; import { PersistedZustandStore, ZustandDefinition } from "./zustand"; +export interface Token { + access: string; + refresh: string; +} + interface AuthorizationState { - tokens: Record; - getToken(): string | undefined; - setToken(token: string): void; - deleteToken(): void; + tokens: Record; + getToken(): Token | undefined; + setToken(access: string, refresh: string): void; + deleteTokens(): void; isAuthorized(): boolean; } @@ -26,17 +31,18 @@ export const useAuthorizationStore: PersistedZustandStore = getToken() { return get().tokens[UserStore.getCurrentUser()?.id]; }, - setToken(token) { + setToken(access, refresh) { const userId = UserStore.getCurrentUser()?.id; if (userId) { set({ - tokens: Object.assign(get().tokens, { - [userId]: token, - }), + tokens: { + ...get().tokens, + [userId]: { access, refresh }, + }, }); } }, - deleteToken() { + deleteTokens() { set({ tokens: {} }); }, isAuthorized() { @@ -45,11 +51,24 @@ export const useAuthorizationStore: PersistedZustandStore = })) as ZustandDefinition, { name: "songspotlight-auth", + version: 1, + migrate(persisted: any, version: number) { + if (version === 0) { + persisted.tokens = Object.fromEntries( + Object.entries(persisted.tokens).map(([userId, access]) => [userId, { + access, + refresh: "", + }]), + ); + } + + return persisted; + }, storage: { async getItem(name: string) { return (await DataStore.get(name)) ?? null; }, - async setItem(name: string, value: string) { + async setItem(name: string, value: unknown) { return await DataStore.set(name, value); }, async removeItem(name: string) { diff --git a/src/equicordplugins/songSpotlight.desktop/settings.tsx b/src/equicordplugins/songSpotlight.desktop/settings.tsx index 2819a7adb..97af8cc3f 100644 --- a/src/equicordplugins/songSpotlight.desktop/settings.tsx +++ b/src/equicordplugins/songSpotlight.desktop/settings.tsx @@ -7,6 +7,7 @@ import { definePluginSettings } from "@api/Settings"; import { makeRange, OptionType } from "@utils/types"; +import { apiConstants } from "./lib/api"; import Settings from "./ui/settings"; export default definePluginSettings({ @@ -18,8 +19,8 @@ export default definePluginSettings({ profileSongsLimit: { type: OptionType.SLIDER, description: "How many songs are shown when initially clicking on a user", - default: 6, - markers: makeRange(1, 6), + default: apiConstants.songLimit, + markers: makeRange(1, apiConstants.songLimit), }, manager: { type: OptionType.COMPONENT, diff --git a/src/equicordplugins/songSpotlight.desktop/ui/settings/index.tsx b/src/equicordplugins/songSpotlight.desktop/ui/settings/index.tsx index ef42841de..7a36d6f73 100644 --- a/src/equicordplugins/songSpotlight.desktop/ui/settings/index.tsx +++ b/src/equicordplugins/songSpotlight.desktop/ui/settings/index.tsx @@ -85,7 +85,7 @@ interface SettingsProps { } export default function Settings({ templateData }: SettingsProps) { - const { isAuthorized, deleteToken } = useAuthorizationStore(); + const { isAuthorized, deleteTokens } = useAuthorizationStore(); const { self } = useSongStore(); const ticked = useRef(false); @@ -172,7 +172,7 @@ export default function Settings({ templateData }: SettingsProps) { ); } + +export function TranslateButton({ + className, + ...props +}: Partial) { + const link = "https://weblate.equicord.org/projects/equicord/"; + return ( + + ); +} diff --git a/src/components/settings/SpecialCard.css b/src/components/settings/SpecialCard.css index f037aadbd..97a9b5030 100644 --- a/src/components/settings/SpecialCard.css +++ b/src/components/settings/SpecialCard.css @@ -26,6 +26,18 @@ background-color: color-mix(in srgb, #c3a3ce 50%, rgb(240 178 240)); } +.vc-translate-button { + overflow: visible !important; + color: black; + background-color: color-mix(in srgb, #575757 50%, rgb(255, 255, 255)); +} + +.vc-translate-button:hover { + overflow: visible !important; + color: black; + background-color: color-mix(in srgb, #575757 50%, rgb(201, 201, 201)); +} + .vc-invite-link { width: 16px; height: 16px; @@ -142,4 +154,14 @@ .vc-special-image { width: 65%; +} + +.vc-translate-modal-icon { + margin: auto; + width: 60px; +} + +.vc-translate-modal-paragraph { + padding: 12px; + text-align: center; } \ No newline at end of file diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index 6125e7667..cd366010c 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -30,7 +30,7 @@ import { ContextMenuApi, Menu, Toasts, UserStore } from "@webpack/common"; import Plugins, { PluginMeta } from "~plugins"; -import { EquicordDonorModal, VencordDonorModal } from "./modals"; +import { EquicordDonorModal, EquicordTranslatorModal, VencordDonorModal } from "./modals"; const CONTRIBUTOR_BADGE = "https://cdn.discordapp.com/emojis/1092089799109775453.png?size=64"; const EQUICORD_CONTRIBUTOR_BADGE = "https://equicord.org/assets/favicon.png"; @@ -253,7 +253,7 @@ export default definePlugin({ ContextMenuApi.openContextMenu(event, () => ); }, onClick() { - return EquicordDonorModal(); + return badge.tooltip === "Equicord Translator" ? EquicordTranslatorModal() : EquicordDonorModal(); }, } satisfies ProfileBadge)); } diff --git a/src/plugins/_api/badges/modals.tsx b/src/plugins/_api/badges/modals.tsx index 275023666..f66ffe060 100644 --- a/src/plugins/_api/badges/modals.tsx +++ b/src/plugins/_api/badges/modals.tsx @@ -9,7 +9,7 @@ import { Flex } from "@components/Flex"; import { HeadingPrimary } from "@components/Heading"; import { Heart } from "@components/Heart"; import { Paragraph } from "@components/Paragraph"; -import { DonateButton } from "@components/settings"; +import { DonateButton, TranslateButton } from "@components/settings"; import { Margins } from "@utils/margins"; import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal"; @@ -72,7 +72,6 @@ export function EquicordDonorModal() { const modalKey = openModal(props => ( { closeModal(modalKey); - // Will get my own in the future VencordNative.native.openExternal("https://github.com/sponsors/thororen1234"); }}> @@ -123,3 +122,47 @@ export function EquicordDonorModal() { )); } + +export function EquicordTranslatorModal() { + const modalKey = openModal(props => ( + { + closeModal(modalKey); + }}> + + + + + Equicord Translator + + + + + + + +
+ + Awarded to contributors who expand Equicordโ€™s language support by translating content for the community. + +
+
+ + + + + +
+
+ )); +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index e0fef9853..34f87b66b 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1263,7 +1263,15 @@ export const EquicordDevs = Object.freeze({ Leon135: { name: "Leon135", id: 309275452231385088n, - } + }, + bbpltergiest: { + name: "bbpltergiest", + id: 279448683672502274n, + }, + mshl: { + name: "m.shl", + id: 1025245410224263258n, + }, } satisfies Record); // iife so #__PURE__ works correctly From b97cd3e9afad05f6e58c70bebc5332a4401b4016 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:03:22 -0500 Subject: [PATCH 11/29] fix translate badge url --- src/plugins/_api/badges/modals.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/_api/badges/modals.tsx b/src/plugins/_api/badges/modals.tsx index f66ffe060..135bb54ac 100644 --- a/src/plugins/_api/badges/modals.tsx +++ b/src/plugins/_api/badges/modals.tsx @@ -147,7 +147,7 @@ export function EquicordTranslatorModal() {
From f81bddaeb8d65bb028d3f366cf3c0939134714da Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:05:18 -0500 Subject: [PATCH 12/29] fix lint colors --- src/components/settings/SpecialCard.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/settings/SpecialCard.css b/src/components/settings/SpecialCard.css index 97a9b5030..af663475d 100644 --- a/src/components/settings/SpecialCard.css +++ b/src/components/settings/SpecialCard.css @@ -29,13 +29,13 @@ .vc-translate-button { overflow: visible !important; color: black; - background-color: color-mix(in srgb, #575757 50%, rgb(255, 255, 255)); + background-color: color-mix(in srgb, #575757 50%, rgb(255 255 255)); } .vc-translate-button:hover { overflow: visible !important; color: black; - background-color: color-mix(in srgb, #575757 50%, rgb(201, 201, 201)); + background-color: color-mix(in srgb, #575757 50%, rgb(201 201 201)); } .vc-invite-link { From 97b06bf1447a318372a409dfd0768451ac0e6ca8 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:27:12 -0500 Subject: [PATCH 13/29] Revert "AlwaysAnimate: Update Nameplate Patch" This reverts commit 00d797b77c4d43847858cbdf202e8c8e4e06e6d3. --- src/plugins/alwaysAnimate/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/alwaysAnimate/index.ts b/src/plugins/alwaysAnimate/index.ts index 48d1335aa..3c26219de 100644 --- a/src/plugins/alwaysAnimate/index.ts +++ b/src/plugins/alwaysAnimate/index.ts @@ -92,7 +92,7 @@ export default definePlugin({ find: ".MINI_PREVIEW,[", predicate: () => settings.store.nameplates, replacement: { - match: /animate:\i,loop:!0===\i&&!0===\i/, + match: /animate:\i,loop:(?:!0===\i(?:&&)?){2}/, replace: "animate:true,loop:true" }, }, From adb016ce621b47238d88395552bfd44ca367d6a0 Mon Sep 17 00:00:00 2001 From: oma <57739965+Eazvy@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:48:10 -0500 Subject: [PATCH 14/29] contributor modal author fix (#873) --- .../settings/tabs/plugins/ContributorModal.css | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/settings/tabs/plugins/ContributorModal.css b/src/components/settings/tabs/plugins/ContributorModal.css index f35d49f57..9cfcb4b5a 100644 --- a/src/components/settings/tabs/plugins/ContributorModal.css +++ b/src/components/settings/tabs/plugins/ContributorModal.css @@ -5,17 +5,19 @@ .vc-author-modal-header { display: flex; align-items: center; + gap: 8px; margin-bottom: 1em; } .vc-author-modal-name { text-transform: none; + margin: 0; flex-grow: 0; background: var(--background-base-lowest); border-radius: 0 9999px 9999px 0; padding: 6px 0.8em 6px 0.5em; font-size: 20px; - height: 20px; + line-height: 1; position: relative; text-wrap: nowrap; } @@ -35,9 +37,17 @@ } .vc-author-modal-avatar { + flex-shrink: 0; + transform: translateX(4px); + position: relative; + z-index: 2; height: 32px; width: 32px; border-radius: 50%; + border: 2px solid var(--background-primary); + box-shadow: + 0 0 0 1px rgb(255 255 255 / 18%), + 0 2px 6px rgb(0 0 0 / 35%); } .vc-author-modal-root+[class*="footer"] { From 1abdfb4c6a0f6acac6a8abad64126254bed4f532 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 4 Mar 2026 14:57:08 -0500 Subject: [PATCH 15/29] Fix Finds --- src/equicordplugins/_api/userArea.ts | 2 +- src/equicordplugins/declutter/index.tsx | 2 +- src/equicordplugins/musicControls/index.tsx | 2 +- src/plugins/decor/index.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/equicordplugins/_api/userArea.ts b/src/equicordplugins/_api/userArea.ts index 4f0d731c6..53cf52cde 100644 --- a/src/equicordplugins/_api/userArea.ts +++ b/src/equicordplugins/_api/userArea.ts @@ -19,7 +19,7 @@ export default definePlugin({ patches: [ { - find: ".WIDGETS_RTC_UPSELL_COACHMARK),", + find: ".DISPLAY_NAME_STYLES_COACHMARK),", replacement: [ { match: /(?<=className:(\i)\.\i,style:\i,)children:\[/, diff --git a/src/equicordplugins/declutter/index.tsx b/src/equicordplugins/declutter/index.tsx index a88a0fcd3..0eb0d2a88 100644 --- a/src/equicordplugins/declutter/index.tsx +++ b/src/equicordplugins/declutter/index.tsx @@ -172,7 +172,7 @@ export default definePlugin({ }, { // Always show username - find: ".NITRO_PRIVACY_PERK_BETA_COACHMARK));", + find: ".DISPLAY_NAME_STYLES_COACHMARK),", replacement: { match: /hoverText:(\i),forceHover:\i,children:/g, replace: "hoverText:$1,forceHover:!0,children:" diff --git a/src/equicordplugins/musicControls/index.tsx b/src/equicordplugins/musicControls/index.tsx index 0054b321b..dcf547e71 100644 --- a/src/equicordplugins/musicControls/index.tsx +++ b/src/equicordplugins/musicControls/index.tsx @@ -52,7 +52,7 @@ export default definePlugin({ patches: [ { - find: ".WIDGETS_RTC_UPSELL_COACHMARK),", + find: ".DISPLAY_NAME_STYLES_COACHMARK),", replacement: { // react.jsx)(AccountPanel, { ..., showTaglessAccountPanel: blah }) match: /(?<=\i\.jsxs?\)\()(\i),{(?=[^}]*?userTag:\i,occluded:)/, diff --git a/src/plugins/decor/index.tsx b/src/plugins/decor/index.tsx index 7eb415ca6..ea6a1e610 100644 --- a/src/plugins/decor/index.tsx +++ b/src/plugins/decor/index.tsx @@ -87,7 +87,7 @@ export default definePlugin({ }, // Current user area, at bottom of channels/dm list { - find: ".WIDGETS_RTC_UPSELL_COACHMARK),", + find: ".DISPLAY_NAME_STYLES_COACHMARK),", replacement: [ // Use Decor avatar decoration hook { From 0e05560f41b92fcd2234ac4ccfa47cd73d943623 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 4 Mar 2026 18:35:54 -0500 Subject: [PATCH 16/29] Fix Patches & Add favorite servers to fakenitro (might work) --- src/plugins/betterFolders/index.tsx | 2 +- src/plugins/experiments/index.tsx | 8 -------- src/plugins/fakeNitro/index.tsx | 18 ++++++++++++++++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index fa07d51a5..d2566ce36 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -265,7 +265,7 @@ export default definePlugin({ // Create the isBetterFolders and betterFoldersExpandedIds variables 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,)/, + match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i)/, replace: "$&,isBetterFolders,betterFoldersExpandedIds" }, // Export the isBetterFolders and betterFoldersExpandedIds variable to the Guild List component diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 2ebb81f2d..6035f0507 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -87,14 +87,6 @@ export default definePlugin({ replace: (_, rest) => `${rest}onClick:()=>{}` } }, - // Make the Favourites Server experiment allow favouriting DMs and threads - { - find: "useCanFavoriteChannel", - replacement: { - match: /(?<=isFavorite\(\i\.id\).{0,5})\i\.isThread\(\)/, - replace: "false", - } - }, // Enable experiment embed on sent experiment links { find: "Clear Treatment ", diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 5a776bd6d..cd00ab4a9 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -141,7 +141,12 @@ const settings = definePluginSettings({ description: "Whether to disable the embed permission check when sending fake emojis and stickers", type: OptionType.BOOLEAN, default: false - } + }, + enableFavoriteServersBypass: { + description: "Allow favoriting servers without nitro", + type: OptionType.BOOLEAN, + default: true + }, }); function hasPermission(channelId: string, permission: bigint) { @@ -376,7 +381,16 @@ export default definePlugin({ match: /(?<=type:"(?:SOUNDBOARD_SOUNDS_RECEIVED|GUILD_SOUNDBOARD_SOUND_CREATE|GUILD_SOUNDBOARD_SOUND_UPDATE|GUILD_SOUNDBOARD_SOUNDS_UPDATE)".+?available:)\i\.available/g, replace: "true" } - } + }, + // Might bypass favorites req + { + find: ".favoriteAdded,notifyFavoriteAdded:", + predicate: () => settings.store.enableFavoriteServersBypass, + replacement: { + match: /(?<=getCurrentUser\(\)\),\i=).{0,25}\.PremiumTypes.TIER_2\)/, + replace: "true", + } + }, ], get guildId() { From 69024ef6336015b54c638e510eb282db88b9dc6f Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 4 Mar 2026 18:52:54 -0500 Subject: [PATCH 17/29] remove favorites bypass because its broken --- src/plugins/fakeNitro/index.tsx | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index cd00ab4a9..10559321d 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -142,11 +142,6 @@ const settings = definePluginSettings({ type: OptionType.BOOLEAN, default: false }, - enableFavoriteServersBypass: { - description: "Allow favoriting servers without nitro", - type: OptionType.BOOLEAN, - default: true - }, }); function hasPermission(channelId: string, permission: bigint) { @@ -382,15 +377,6 @@ export default definePlugin({ replace: "true" } }, - // Might bypass favorites req - { - find: ".favoriteAdded,notifyFavoriteAdded:", - predicate: () => settings.store.enableFavoriteServersBypass, - replacement: { - match: /(?<=getCurrentUser\(\)\),\i=).{0,25}\.PremiumTypes.TIER_2\)/, - replace: "true", - } - }, ], get guildId() { From 876e8b9d197f232d3b098cc86c39c84ad4f0743b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 5 Mar 2026 15:40:33 -0500 Subject: [PATCH 18/29] BetterSettings: Actually use organizemenu setting --- src/plugins/betterSettings/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index 1f2578b94..2dde0aa99 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -168,6 +168,7 @@ export default definePlugin({ }, { // Settings cog context menu find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}", + predicate: () => settings.store.organizeMenu, replacement: [ { match: /(\i)=(.{0,20}openUserSettings\)\(\i\)\));/, From 9567a0af677e55b6d2eb20d9011cc80697a5295c Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 5 Mar 2026 17:33:12 -0500 Subject: [PATCH 19/29] SongSpotlight Patch File --- .../songSpotlight.desktop/lib/oauth2.tsx | 5 ++--- src/plugins/betterSettings/index.tsx | 17 ----------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/equicordplugins/songSpotlight.desktop/lib/oauth2.tsx b/src/equicordplugins/songSpotlight.desktop/lib/oauth2.tsx index 8780a8df0..7bf8e2847 100644 --- a/src/equicordplugins/songSpotlight.desktop/lib/oauth2.tsx +++ b/src/equicordplugins/songSpotlight.desktop/lib/oauth2.tsx @@ -37,10 +37,9 @@ export function presentOAuth2Modal() { if (!access) throw "Access token is missing"; const refresh = res.headers.get("X-Refresh-Token"); - // STUB uncomment this once API is fully rolled out - // if (!refresh) throw "Refresh token is missing"; + if (!refresh) throw "Refresh token is missing"; - useAuthorizationStore.getState().setToken(access, refresh || ""); + useAuthorizationStore.getState().setToken(access, refresh); getData(); showToast("Successfully authorized!", Toasts.Type.SUCCESS); diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index 2dde0aa99..2ff627903 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -17,18 +17,6 @@ import type { HTMLAttributes } from "react"; import fullHeightStyle from "./fullHeightContext.css?managed"; -type SettingsEntry = { - ariaLabel?: string; - element?: any; - label?: string; - newIndicator?: any; - newIndicatorDismissibleContentTypes?: number[]; - predicate?: () => boolean; - searchableTitles?: string[]; - section: string, - tabPredicate?: () => boolean; -}; - const cl = classNameFactory(""); const Classes = findCssClassesLazy("animating", "baseLayer", "bg", "layer", "layers"); @@ -53,11 +41,6 @@ const settings = definePluginSettings({ } }); -interface TransformedSettingsEntry { - section: string; - items: SettingsEntry[]; -} - interface LayerProps extends HTMLAttributes { mode: "SHOWN" | "HIDDEN"; baseLayer?: boolean; From 077262d9d1419d50035f147df885bf53a6c88ebf Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 5 Mar 2026 18:01:54 -0500 Subject: [PATCH 20/29] EquicordHelper: Remove Always Showing Open Legacy Settings --- src/equicordplugins/equicordHelper/index.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/equicordplugins/equicordHelper/index.tsx b/src/equicordplugins/equicordHelper/index.tsx index 59cce166f..3c6bb883f 100644 --- a/src/equicordplugins/equicordHelper/index.tsx +++ b/src/equicordplugins/equicordHelper/index.tsx @@ -188,16 +188,6 @@ export default definePlugin({ replace: "{_enableHangStatus:$1=false," } }, - // Always show open legacy settings - { - find: ".DEVELOPER_SECTION,", - replacement: [ - { - match: /\i\.\i\.isDeveloper/, - replace: "true" - }, - ] - }, // Force Role Icon { find: "Message Username", From fbaf346bb68fd8d821c73224436344cbc008faaa Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 5 Mar 2026 19:39:24 -0500 Subject: [PATCH 21/29] Fix Find --- src/equicordplugins/favouriteAnything/components.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/favouriteAnything/components.tsx b/src/equicordplugins/favouriteAnything/components.tsx index ac38c411e..847e96528 100644 --- a/src/equicordplugins/favouriteAnything/components.tsx +++ b/src/equicordplugins/favouriteAnything/components.tsx @@ -23,7 +23,7 @@ const FavoriteButton = findComponentByCodeLazy("#{intl::GIF const SendIcon = findComponentByCodeLazy("M6.6 10.02 14 11.4a.6.6"); const DmChannel: Channel & { new(base?: Partial): Channel; } = findByPropsLazy("fromServer", "sortRecipients"); -const MessageClass: Message & { new(base?: Partial): Message; } = findByCodeLazy(";mentionChannels;mentionGames;"); +const MessageClass: Message & { new(base?: Partial): Message; } = findByCodeLazy(";mentionRoles;mentionChannels;"); const Classes = findCssClassesLazy("gifFavoriteButton", "ctaButtonContainer"); From b7d7a6fda932f3d5a95f28846f63f6fe37b48378 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 5 Mar 2026 19:50:21 -0500 Subject: [PATCH 22/29] PlatformSpoofer: vcicon just works now :shrug: --- src/equicordplugins/platformSpoofer/index.tsx | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/equicordplugins/platformSpoofer/index.tsx b/src/equicordplugins/platformSpoofer/index.tsx index d6366bbf8..aab45545a 100644 --- a/src/equicordplugins/platformSpoofer/index.tsx +++ b/src/equicordplugins/platformSpoofer/index.tsx @@ -67,13 +67,6 @@ export default definePlugin({ replace: "$1{...$2,...$self.getPlatform(true)},presence" } }, - { - find: "#{intl::POPOUT_STAY_ON_TOP}),icon:", - replacement: { - match: /(?<=CallTile.{0,15}\.memo\((\i)=>\{)/, - replace: "$1.platform = $self.getPlatform(false, $1?.participantUserId)?.vcIcon || $1?.platform;" - } - }, { find: '("AppSkeleton");', replacement: { @@ -88,19 +81,19 @@ export default definePlugin({ if (bypass || userId === UserStore.getCurrentUser().id) { switch (platform) { case "desktop": - return { browser: "Discord Client", vcIcon: 0 }; + return { browser: "Discord Client" }; case "web": - return { browser: "Discord Web", vcIcon: 0 }; + return { browser: "Discord Web" }; case "ios": - return { browser: "Discord iOS", vcIcon: 1 }; + return { browser: "Discord iOS" }; case "android": - return { browser: "Discord Android", vcIcon: 1 }; + return { browser: "Discord Android" }; case "xbox": - return { browser: "Discord Embedded", vcIcon: 2 }; + return { browser: "Discord Embedded" }; case "playstation": - return { browser: "Discord Embedded", vcIcon: 3 }; + return { browser: "Discord Embedded" }; case "vr": - return { browser: "Discord VR", vcIcon: 4 }; + return { browser: "Discord VR" }; default: return null; } From c6e0ef1a111b26ec0fb49ff714f00bea4e16d791 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 4 Mar 2026 21:06:54 -0500 Subject: [PATCH 23/29] fix BetterFolders, SpotifyControls, GameActivityToggle --- src/plugins/_core/settings.tsx | 8 -------- src/plugins/betterFolders/index.tsx | 2 +- src/plugins/decor/index.tsx | 2 +- src/plugins/experiments/index.tsx | 8 -------- src/plugins/gameActivityToggle/index.tsx | 2 +- src/plugins/spotifyControls/index.tsx | 2 +- 6 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 55d6d7d18..c1781edfc 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -134,14 +134,6 @@ export default definePlugin({ } ] }, - { - find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}", - replacement: { - // Skip the check Discord performs to make sure the section being selected in the user settings context menu is valid - match: /null!=\(\i=Object.values\(\i\.\i\).{0,50}?&&(?=\(0,\i\.openUserSettings\)\(\i,\{section:\i)/, - replace: "" - } - }, { find: ".buildLayout().map", replacement: { diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index fe312f1a9..ad23c1e3d 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -149,7 +149,7 @@ export default definePlugin({ // Create the isBetterFolders and betterFoldersExpandedIds variables 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,)/, + match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i)/, replace: "$&,isBetterFolders,betterFoldersExpandedIds" }, // Export the isBetterFolders and betterFoldersExpandedIds variable to the Guild List component diff --git a/src/plugins/decor/index.tsx b/src/plugins/decor/index.tsx index de751a2f0..e66bd1e7e 100644 --- a/src/plugins/decor/index.tsx +++ b/src/plugins/decor/index.tsx @@ -87,7 +87,7 @@ export default definePlugin({ }, // Current user area, at bottom of channels/dm list { - find: ".WIDGETS_RTC_UPSELL_COACHMARK),", + find: ".DISPLAY_NAME_STYLES_COACHMARK),", replacement: [ // Use Decor avatar decoration hook { diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 5eb8adf33..102c5bb2e 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -96,14 +96,6 @@ export default definePlugin({ replace: (_, rest) => `${rest}onClick:()=>{}` } }, - // Make the Favourites Server experiment allow favouriting DMs and threads - { - find: "useCanFavoriteChannel", - replacement: { - match: /(?<=isFavorite\(\i\.id\).{0,5})\i\.isThread\(\)/, - replace: "false", - } - }, // Enable experiment embed on sent experiment links { find: "Clear Treatment ", diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index a6d98746f..0a0e792d3 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -112,7 +112,7 @@ export default definePlugin({ patches: [ { - find: ".WIDGETS_RTC_UPSELL_COACHMARK),", + find: ".DISPLAY_NAME_STYLES_COACHMARK),", replacement: { match: /children:\[(?=.{0,25}?accountContainerRef)/, replace: "children:[$self.GameActivityToggleButton(arguments[0])," diff --git a/src/plugins/spotifyControls/index.tsx b/src/plugins/spotifyControls/index.tsx index 45d47299f..f476165e9 100644 --- a/src/plugins/spotifyControls/index.tsx +++ b/src/plugins/spotifyControls/index.tsx @@ -53,7 +53,7 @@ export default definePlugin({ }, patches: [ { - find: ".WIDGETS_RTC_UPSELL_COACHMARK),", + find: ".DISPLAY_NAME_STYLES_COACHMARK),", replacement: { // react.jsx)(AccountPanel, { ..., showTaglessAccountPanel: blah }) match: /(?<=\i\.jsxs?\)\()(\i),{(?=[^}]*?userTag:\i,occluded:)/, From 15e92a49a1c31ce2d40c10ea1770da53b7c8e1bd Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 6 Mar 2026 06:08:51 +0100 Subject: [PATCH 24/29] 1.14.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 252d66068..aa0e28f36 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.14.3", + "version": "1.14.4", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 7948d56c00a03ef958fb9dc282db19bbed8e4797 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:38:08 -0500 Subject: [PATCH 25/29] InstantScreenShare: Stop Stream If Already Streaming --- .../instantScreenshare/index.tsx | 31 +++++++++++++------ src/plugins/betterSettings/index.tsx | 12 +++---- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/equicordplugins/instantScreenshare/index.tsx b/src/equicordplugins/instantScreenshare/index.tsx index 1a29d719f..1d0160048 100644 --- a/src/equicordplugins/instantScreenshare/index.tsx +++ b/src/equicordplugins/instantScreenshare/index.tsx @@ -15,8 +15,9 @@ import { ChannelStore, MediaEngineStore, PermissionsBits, PermissionStore, Selec import { getCurrentMedia, settings } from "./utils"; -let hasStreamed; +let hasStreamed, isStreaming, streamKey; const startStream = findByCodeLazy('type:"STREAM_START"'); +const stopStream = findByCodeLazy('type:"STREAM_STOP"'); const StreamPreviewSettings = getUserSettingLazy("voiceAndVideo", "disableStreamPreviews")!; const ApplicationStreamingSettingsStore = findStoreLazy("ApplicationStreamingSettingsStore"); @@ -44,14 +45,18 @@ async function autoStartStream(instant = true) { let sourceId = streamMedia.id; if (streamMedia.type === "video_device") sourceId = `camera:${streamMedia.id}`; - startStream(channel.guild_id ?? null, selected, { - "pid": null, - "sourceId": sourceId, - "sourceName": streamMedia.name, - "audioSourceId": streamMedia.name, - "sound": soundshareEnabled, - "previewDisabled": preview - }); + if (isStreaming && streamKey.endsWith(UserStore.getCurrentUser().id)) { + stopStream(streamKey); + } else { + startStream(channel.guild_id ?? null, selected, { + "pid": null, + "sourceId": sourceId, + "sourceName": streamMedia.name, + "audioSourceId": streamMedia.name, + "sound": soundshareEnabled, + "previewDisabled": preview + }); + } } export default definePlugin({ @@ -122,6 +127,14 @@ export default definePlugin({ break; } + }, + STREAM_CREATE: d => { + streamKey = d; + isStreaming = true; + }, + STREAM_DELETE: d => { + streamKey = d; + isStreaming = false; } }, diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index 2ff627903..7360c32a3 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -13,7 +13,7 @@ import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; import { findCssClassesLazy } from "@webpack"; import { ComponentDispatch, FocusLock, Menu, useEffect, useRef } from "@webpack/common"; -import type { HTMLAttributes } from "react"; +import type { HTMLAttributes, ReactNode } from "react"; import fullHeightStyle from "./fullHeightContext.css?managed"; @@ -154,8 +154,8 @@ export default definePlugin({ predicate: () => settings.store.organizeMenu, replacement: [ { - match: /(\i)=(.{0,20}openUserSettings\)\(\i\)\));/, - replace: "$1=$self.transformSettingsElements($2);", + match: /children:\[(\i),(?<=\1=.{0,30}\.openUserSettings.+?)/, + replace: "children:[$self.transformSettingsEntries($1),", }, ] }, @@ -179,8 +179,8 @@ export default definePlugin({ return ; }, - transformSettingsElements(list) { - const items: any[] = []; + transformSettingsEntries(list) { + const items: ReactNode[] = []; for (const item of list) { const { key, props } = item; @@ -199,7 +199,7 @@ export default definePlugin({ } else if (key.endsWith("_section") && props.label) { items.push( - {this.transformSettingsElements(props.children)} + {this.transformSettingsEntries(props.children)} ); } else { From 14fb4f8c029175bf1571a625b55ab54879c8a9bf Mon Sep 17 00:00:00 2001 From: g <83366426+bbpltergiest@users.noreply.github.com> Date: Fri, 6 Mar 2026 17:01:40 -0300 Subject: [PATCH 26/29] more informative plugins description (#882) * description improvement * HopOn: fix description --- src/equicordplugins/hopOn/index.tsx | 2 +- src/equicordplugins/moyai/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/hopOn/index.tsx b/src/equicordplugins/hopOn/index.tsx index 46e5326c9..cd0de30ed 100644 --- a/src/equicordplugins/hopOn/index.tsx +++ b/src/equicordplugins/hopOn/index.tsx @@ -31,7 +31,7 @@ const settings = definePluginSettings({ }); export default definePlugin({ name: "HopOn", - description: "Hop on Fortnite or Hop on bloons :3", + description: "Hop on! Opens a configurable URL when a message matches a custom regex in the current channel.", authors: [Devs.ImLvna], settings, flux: { diff --git a/src/equicordplugins/moyai/index.ts b/src/equicordplugins/moyai/index.ts index 2a5ad52d1..4c6d79391 100644 --- a/src/equicordplugins/moyai/index.ts +++ b/src/equicordplugins/moyai/index.ts @@ -90,7 +90,7 @@ const settings = definePluginSettings({ export default definePlugin({ name: "Moyai", authors: [Devs.Megu, Devs.Nuckyz], - description: "๐Ÿ—ฟ๐Ÿ—ฟ๐Ÿ—ฟ๐Ÿ—ฟ๐Ÿ—ฟ๐Ÿ—ฟ๐Ÿ—ฟ๐Ÿ—ฟ", + description: "Plays a ๐Ÿ—ฟ sound effect whenever a moyai emoji is sent, reacted, or used as a voice effect in your current channel.", settings, flux: { From 95fdc507665d8f3ef8df0d82966f8b3f2426df27 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:10:12 -0500 Subject: [PATCH 27/29] Fix Patch --- src/plugins/noDevtoolsWarning/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/noDevtoolsWarning/index.ts b/src/plugins/noDevtoolsWarning/index.ts index e897e5793..5c35fa67b 100644 --- a/src/plugins/noDevtoolsWarning/index.ts +++ b/src/plugins/noDevtoolsWarning/index.ts @@ -26,7 +26,7 @@ export default definePlugin({ patches: [{ find: "setDevtoolsCallbacks", replacement: { - match: /if\(null!=\i&&"0.0.0"===\i\.remoteApp\.getVersion\(\)\)/, + match: /if\(null!=\i&&"0.0.0"===\i\.app\.getVersion\(\)\)/, replace: "if(true)" } }] From 34feac8a6ce210a8b693edbd0da679ac7767473a Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:18:45 -0500 Subject: [PATCH 28/29] Fix Find --- src/plugins/noMosaic/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/noMosaic/index.ts b/src/plugins/noMosaic/index.ts index 6ff34a350..4ec64cc39 100644 --- a/src/plugins/noMosaic/index.ts +++ b/src/plugins/noMosaic/index.ts @@ -27,7 +27,7 @@ export default definePlugin({ patches: [ { - find: '=>"IMAGE"===', + find: '"PLAINTEXT_PREVIEW":"OTHER"', replacement: { match: /=>"IMAGE"===\i\|\|"VIDEO"===\i(?:\|\|("VISUAL_PLACEHOLDER"===\i))?;/, replace: (_, visualPlaceholderPred) => visualPlaceholderPred != null ? `=>${visualPlaceholderPred};` : "=>false;" From 4a3f0c9e21697924d49ba5e1de237c833aa4695f Mon Sep 17 00:00:00 2001 From: oma <57739965+Eazvy@users.noreply.github.com> Date: Fri, 6 Mar 2026 20:09:21 -0500 Subject: [PATCH 29/29] Fix FavouriteAnything attachment preview crash (#884) --- .../favouriteAnything/components.tsx | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/equicordplugins/favouriteAnything/components.tsx b/src/equicordplugins/favouriteAnything/components.tsx index 847e96528..0f6db374f 100644 --- a/src/equicordplugins/favouriteAnything/components.tsx +++ b/src/equicordplugins/favouriteAnything/components.tsx @@ -7,9 +7,9 @@ import { BaseText } from "@components/BaseText"; import { Button } from "@components/Button"; import { LazyComponentWrapper } from "@utils/lazyReact"; -import { Channel, Message, MessageAttachment, ScrollerBaseRef } from "@vencord/discord-types"; +import { Message, MessageAttachment, ScrollerBaseRef } from "@vencord/discord-types"; import { ChannelType } from "@vencord/discord-types/enums"; -import { findByCodeLazy, findByPropsLazy, findComponentByCode, findComponentByCodeLazy, findCssClassesLazy, proxyLazyWebpack } from "@webpack"; +import { findByCodeLazy, findComponentByCode, findComponentByCodeLazy, findCssClassesLazy, proxyLazyWebpack } from "@webpack"; import { ChannelStore, ExpressionPickerStore, ListScrollerThin, lodash, PermissionsBits, PermissionStore, React, useCallback, useEffect, useMemo, useRef, useState, useStateFromStores } from "@webpack/common"; import { ReactNode } from "react"; @@ -22,11 +22,24 @@ const ManaSearchBar = findComponentByCodeLazy("#{intl::SEARC const FavoriteButton = findComponentByCodeLazy("#{intl::GIF_TOOLTIP_ADD_TO_FAVORITES}"); const SendIcon = findComponentByCodeLazy("M6.6 10.02 14 11.4a.6.6"); -const DmChannel: Channel & { new(base?: Partial): Channel; } = findByPropsLazy("fromServer", "sortRecipients"); -const MessageClass: Message & { new(base?: Partial): Message; } = findByCodeLazy(";mentionRoles;mentionChannels;"); +const createChannelRecordFromServer = findByCodeLazy(".GUILD_TEXT]", "fromServer)"); +const createMessageRecord = findByCodeLazy(".createFromServer(", ".isBlockedForMessage", "messageReference:"); const Classes = findCssClassesLazy("gifFavoriteButton", "ctaButtonContainer"); +function createPreviewMessage(attachment: MessageAttachment, channelId: string) { + const previewMessage = { + id: `favourite-anything-preview-${attachment.id}`, + attachments: [attachment], + channel_id: channelId, + content: "", + type: 0, + timestamp: new Date().toISOString() + }; + + return createMessageRecord(previewMessage) as Message; +} + export const AttachmentPreview = proxyLazyWebpack(() => { // findComponentByCodeLazy doesn't work properly with component classes, this must be kept within the lazy scope const MessageComponent = findComponentByCode("this.renderAttachments") as LazyComponentWrapper; @@ -37,12 +50,12 @@ export const AttachmentPreview = proxyLazyWebpack(() => { } } - const channel = Object.freeze(new DmChannel({ id: "0", type: ChannelType.GUILD_TEXT })); + const channel = Object.freeze(createChannelRecordFromServer({ id: "0", type: ChannelType.GUILD_TEXT })); return function AttachmentPreview({ attachment }: AttachmentsComponentProps) { const message = useMemo( - () => new MessageClass({ attachments: [attachment], channel_id: channel.id }), - [attachment] + () => createPreviewMessage(attachment, channel.id), + [attachment, channel.id] ); return (