From 5cb2ce51ac741985aed8a5a9d02ae0a60ce16bc5 Mon Sep 17 00:00:00 2001
From: oma <57739965+Eazvy@users.noreply.github.com>
Date: Mon, 2 Mar 2026 20:19:19 -0500
Subject: [PATCH 01/41] VoiceButtons: fix crash when user is undefined (#862)
---
src/equicordplugins/voiceButtons/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/equicordplugins/voiceButtons/index.tsx b/src/equicordplugins/voiceButtons/index.tsx
index a8b18c885..bc4f86cb4 100644
--- a/src/equicordplugins/voiceButtons/index.tsx
+++ b/src/equicordplugins/voiceButtons/index.tsx
@@ -24,7 +24,7 @@ export default definePlugin({
replacement: [
{
match: /\[\i\.\i\]:\i\}\),children:\[/,
- replace: "$&$self.renderButtons(arguments[0].user),"
+ replace: "$&$self.renderButtons(arguments[0]?.user),"
}
]
}
From b3b07ee2982e92a052ceacb9364efc9be1d0b57b Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Mon, 2 Mar 2026 21:23:47 -0500
Subject: [PATCH 02/41] Fix Patch
---
package.json | 2 +-
pnpm-lock.yaml | 164 +++++++++++++++++++++++++++---
src/plugins/_api/nicknameIcons.ts | 2 +-
3 files changed, 151 insertions(+), 17 deletions(-)
diff --git a/package.json b/package.json
index f1e93d13c..80a673c7c 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
"@ffmpeg/ffmpeg": "^0.12.10",
"@ffmpeg/util": "^0.12.1",
"@intrnl/xxhash64": "^0.1.2",
- "@song-spotlight/api": "^2.0.0",
+ "@song-spotlight/api": "^2.0.1",
"@streamparser/json": "^0.0.22",
"@types/less": "^3.0.8",
"@types/stylus": "^0.48.42",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a877bef20..c59fd41ef 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,8 +26,8 @@ importers:
specifier: ^0.1.2
version: 0.1.2
'@song-spotlight/api':
- specifier: ^2.0.0
- version: 2.0.0(typescript@5.9.3)
+ specifier: ^2.0.1
+ version: 2.0.1(typescript@5.9.3)
'@streamparser/json':
specifier: ^0.0.22
version: 0.0.22
@@ -721,8 +721,8 @@ packages:
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
- '@song-spotlight/api@2.0.0':
- resolution: {integrity: sha512-a3gyrSojd3BzwloxZHyDXA7HhjmCNQZOYATh7AZJyCXIqilBMwFPgQ2FVkUpzf6MzZhN9emeN6OGZOJb/rkECw==}
+ '@song-spotlight/api@2.0.1':
+ resolution: {integrity: sha512-nxBJch8cGp10NYeJSp3DCPS/LYmlpcs9VCHrHzgCHFRICTKWhlpiUZgrACP9TmQ8RQsE4B9e23BcQDaqWqPCGg==}
peerDependencies:
typescript: ^5
@@ -941,6 +941,10 @@ packages:
resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
engines: {node: '>= 0.4'}
+ array-includes@3.1.9:
+ resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
+ engines: {node: '>= 0.4'}
+
array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
@@ -957,6 +961,10 @@ packages:
resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
engines: {node: '>= 0.4'}
+ array.prototype.flat@1.3.3:
+ resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
+ engines: {node: '>= 0.4'}
+
array.prototype.flatmap@1.3.3:
resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
engines: {node: '>= 0.4'}
@@ -1258,6 +1266,10 @@ packages:
resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
engines: {node: '>= 0.4'}
+ es-abstract@1.24.1:
+ resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==}
+ engines: {node: '>= 0.4'}
+
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
@@ -1322,8 +1334,8 @@ packages:
eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
- eslint-module-utils@2.12.0:
- resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==}
+ eslint-module-utils@2.12.1:
+ resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
@@ -1757,6 +1769,10 @@ packages:
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'}
+ is-negative-zero@2.0.3:
+ resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
+ engines: {node: '>= 0.4'}
+
is-number-object@1.1.1:
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'}
@@ -1947,6 +1963,9 @@ packages:
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+ minimatch@3.1.5:
+ resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
+
minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -2217,6 +2236,11 @@ packages:
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+ resolve@1.22.11:
+ resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
resolve@1.22.8:
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
hasBin: true
@@ -2340,6 +2364,10 @@ packages:
standalone-electron-types@34.2.0:
resolution: {integrity: sha512-+BIrNe0TdZBBBRS3G/F7cPbuBipSZylSjSrJu9+sjw84+vz36a5oZ7l9NeH7aXgMWmPzPZKsTm+K0nOIvhENJQ==}
+ stop-iteration-iterator@1.1.0:
+ resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
+ engines: {node: '>= 0.4'}
+
streamx@2.22.0:
resolution: {integrity: sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==}
@@ -2588,6 +2616,10 @@ packages:
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
engines: {node: '>= 0.4'}
+ which-typed-array@1.1.20:
+ resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==}
+ engines: {node: '>= 0.4'}
+
which@1.3.1:
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
hasBin: true
@@ -3006,7 +3038,7 @@ snapshots:
'@rtsao/scc@1.1.0': {}
- '@song-spotlight/api@2.0.0(typescript@5.9.3)':
+ '@song-spotlight/api@2.0.1(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
optionalDependencies:
@@ -3271,6 +3303,17 @@ snapshots:
get-intrinsic: 1.3.0
is-string: 1.1.1
+ array-includes@3.1.9:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ is-string: 1.1.1
+ math-intrinsics: 1.1.0
+
array-union@2.1.0: {}
array.prototype.findlast@1.2.5:
@@ -3287,7 +3330,7 @@ snapshots:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
es-shim-unscopables: 1.1.0
@@ -3299,6 +3342,13 @@ snapshots:
es-abstract: 1.23.9
es-shim-unscopables: 1.1.0
+ array.prototype.flat@1.3.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-shim-unscopables: 1.1.0
+
array.prototype.flatmap@1.3.3:
dependencies:
call-bind: 1.0.8
@@ -3644,6 +3694,63 @@ snapshots:
unbox-primitive: 1.1.0
which-typed-array: 1.1.19
+ es-abstract@1.24.1:
+ dependencies:
+ array-buffer-byte-length: 1.0.2
+ arraybuffer.prototype.slice: 1.0.4
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ data-view-buffer: 1.0.2
+ data-view-byte-length: 1.0.2
+ data-view-byte-offset: 1.0.1
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-set-tostringtag: 2.1.0
+ es-to-primitive: 1.3.0
+ function.prototype.name: 1.1.8
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ get-symbol-description: 1.1.0
+ globalthis: 1.0.4
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+ has-proto: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ internal-slot: 1.1.0
+ is-array-buffer: 3.0.5
+ is-callable: 1.2.7
+ is-data-view: 1.0.2
+ is-negative-zero: 2.0.3
+ is-regex: 1.2.1
+ is-set: 2.0.3
+ is-shared-array-buffer: 1.0.4
+ is-string: 1.1.1
+ is-typed-array: 1.1.15
+ is-weakref: 1.1.1
+ math-intrinsics: 1.1.0
+ object-inspect: 1.13.4
+ object-keys: 1.1.1
+ object.assign: 4.1.7
+ own-keys: 1.0.1
+ regexp.prototype.flags: 1.5.4
+ safe-array-concat: 1.1.3
+ safe-push-apply: 1.0.0
+ safe-regex-test: 1.1.0
+ set-proto: 1.0.0
+ stop-iteration-iterator: 1.1.0
+ string.prototype.trim: 1.2.10
+ string.prototype.trimend: 1.0.9
+ string.prototype.trimstart: 1.0.8
+ typed-array-buffer: 1.0.3
+ typed-array-byte-length: 1.0.3
+ typed-array-byte-offset: 1.0.4
+ typed-array-length: 1.0.7
+ unbox-primitive: 1.1.0
+ which-typed-array: 1.1.20
+
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
@@ -3767,11 +3874,11 @@ snapshots:
dependencies:
debug: 3.2.7
is-core-module: 2.16.1
- resolve: 1.22.8
+ resolve: 1.22.11
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -3784,19 +3891,19 @@ snapshots:
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1):
dependencies:
'@rtsao/scc': 1.1.0
- array-includes: 3.1.8
+ array-includes: 3.1.9
array.prototype.findlastindex: 1.2.6
- array.prototype.flat: 1.3.2
+ array.prototype.flat: 1.3.3
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
eslint: 9.39.1
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1)
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
- minimatch: 3.1.2
+ minimatch: 3.1.5
object.fromentries: 2.0.8
object.groupby: 1.0.3
object.values: 1.2.1
@@ -4276,6 +4383,8 @@ snapshots:
is-map@2.0.3: {}
+ is-negative-zero@2.0.3: {}
+
is-number-object@1.1.1:
dependencies:
call-bound: 1.0.4
@@ -4450,6 +4559,10 @@ snapshots:
dependencies:
brace-expansion: 1.1.12
+ minimatch@3.1.5:
+ dependencies:
+ brace-expansion: 1.1.12
+
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.2
@@ -4524,7 +4637,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.1
object.values@1.2.1:
dependencies:
@@ -4734,6 +4847,12 @@ snapshots:
resolve-pkg-maps@1.0.0: {}
+ resolve@1.22.11:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
resolve@1.22.8:
dependencies:
is-core-module: 2.16.1
@@ -4875,6 +4994,11 @@ snapshots:
dependencies:
'@types/node': 22.13.13
+ stop-iteration-iterator@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ internal-slot: 1.1.0
+
streamx@2.22.0:
dependencies:
fast-fifo: 1.3.2
@@ -5239,6 +5363,16 @@ snapshots:
gopd: 1.2.0
has-tostringtag: 1.0.2
+ which-typed-array@1.1.20:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ for-each: 0.3.5
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+
which@1.3.1:
dependencies:
isexe: 2.0.0
diff --git a/src/plugins/_api/nicknameIcons.ts b/src/plugins/_api/nicknameIcons.ts
index 47c7b8905..81423e450 100644
--- a/src/plugins/_api/nicknameIcons.ts
+++ b/src/plugins/_api/nicknameIcons.ts
@@ -15,7 +15,7 @@ export default definePlugin({
{
find: "#{intl::USER_PROFILE_PRONOUNS}",
replacement: {
- match: /(?<=shouldUnderlineOnHover:null.{0,150})children:(\i)/,
+ match: /(?<=shouldUnderlineOnHover:null.{0,200})children:(\i)/,
replace: "children:[...Vencord.Api.NicknameIcons._renderIcons({userId:arguments[0].user?.id}),$1]"
}
}
From 9ce1caf50a17d215bdf88e673f86c8e5441ef8c0 Mon Sep 17 00:00:00 2001
From: oma <57739965+Eazvy@users.noreply.github.com>
Date: Tue, 3 Mar 2026 07:53:39 -0500
Subject: [PATCH 03/41] deClutter (fix divider + title seperators) (#861)
* deClutter: fix divider and title separators
* Description and toggle fix
* indi suggestion on info label for namestyles
* class names yea
@thororen1234 classnames
---
src/equicordplugins/declutter/index.tsx | 53 +++++++++++++------------
src/equicordplugins/declutter/style.css | 19 +++++++++
2 files changed, 47 insertions(+), 25 deletions(-)
create mode 100644 src/equicordplugins/declutter/style.css
diff --git a/src/equicordplugins/declutter/index.tsx b/src/equicordplugins/declutter/index.tsx
index cf4c44dc0..661d94fe6 100644
--- a/src/equicordplugins/declutter/index.tsx
+++ b/src/equicordplugins/declutter/index.tsx
@@ -4,8 +4,13 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+import "./style.css";
+
import { definePluginSettings, migratePluginSetting, migratePluginSettings } from "@api/Settings";
+import { Divider } from "@components/Divider";
import { HeadingSecondary } from "@components/Heading";
+import { Notice } from "@components/Notice";
+import { classNameFactory } from "@utils/css";
import { Devs, EquicordDevs } from "@utils/index";
import definePlugin, { OptionType } from "@utils/types";
@@ -24,6 +29,8 @@ for (const [oldKey, newKey] of migrationsAnammox) {
migratePluginSetting("Declutter", newKey, oldKey);
}
+const cl = classNameFactory("vc-declutter-");
+
export const settings = definePluginSettings({
userProfileHeader: {
type: OptionType.COMPONENT,
@@ -59,11 +66,13 @@ export const settings = definePluginSettings({
default: true,
restartNeeded: true
},
- removeUsernameStyles: {
- type: OptionType.BOOLEAN,
- description: "Remove username colors and effects.",
- default: true,
- restartNeeded: true
+ accessibilityNotice: {
+ type: OptionType.COMPONENT,
+ component: () => (
+
+ Discord already has a built-in username style option in Accessibility settings.
+
+ )
},
friendsListHeader: {
type: OptionType.COMPONENT,
@@ -72,7 +81,7 @@ export const settings = definePluginSettings({
removeShopAboveDM: {
type: OptionType.BOOLEAN,
description: "Remove shops above DMs list.",
- default: true,
+ default: false,
restartNeeded: true,
},
removeQuestsAboveDM: {
@@ -125,17 +134,18 @@ export const settings = definePluginSettings({
function SectionSeparator(title: string) {
return (
- <>
-
- {title}
-
- >
+
);
}
export default definePlugin({
name: "Declutter",
- description: "Declutter discord ui by removing unwanted elements such as profile customizations, shops, quests and more.",
+ description: "Cleans up Discord by removing non-essential UI elements like profile effects, shop tabs, boosts, and more.",
authors: [EquicordDevs.Leon135, Devs.prism, Devs.Kyuuhachi],
settings,
patches: [
@@ -176,20 +186,13 @@ export default definePlugin({
predicate: () => settings.store.removeClanTag,
},
{
- // Username styles and allways show username
+ // Always show username
find: ".NITRO_PRIVACY_PERK_BETA_COACHMARK));",
- replacement: [
- {
- match: /displayNameStyles:(\i),/,
- replace: "displayNameStyles:void 0,",
- predicate: () => settings.store.removeUsernameStyles
- },
- {
- match: /hoverText:(\i),forceHover:\i,children:/g,
- replace: "hoverText:$1,forceHover:!0,children:",
- predicate: () => settings.store.alwaysShowUsername
- },
- ],
+ replacement: {
+ match: /hoverText:(\i),forceHover:\i,children:/g,
+ replace: "hoverText:$1,forceHover:!0,children:"
+ },
+ predicate: () => settings.store.alwaysShowUsername
},
{
// Button tooltips in user area
diff --git a/src/equicordplugins/declutter/style.css b/src/equicordplugins/declutter/style.css
new file mode 100644
index 000000000..f979e2235
--- /dev/null
+++ b/src/equicordplugins/declutter/style.css
@@ -0,0 +1,19 @@
+.vc-declutter-accessibility-notice {
+ margin-top: 8px;
+ margin-bottom: 8px;
+}
+
+.vc-declutter-section-separator {
+ margin-top: 12px;
+ margin-bottom: 6px;
+}
+
+.vc-declutter-section-title {
+ margin-top: 14px;
+ margin-bottom: 0;
+ font-size: 16px;
+ line-height: 20px;
+ text-transform: uppercase;
+ letter-spacing: 0.02em;
+ color: var(--text-muted);
+}
From eca90777d122f4fe50ca6e80815dfa93035536b9 Mon Sep 17 00:00:00 2001
From: prism
Date: Tue, 3 Mar 2026 16:18:41 +0100
Subject: [PATCH 04/41] add plugin: ClickableRoles (#864)
* add plugin: ClickableRoles
* Update index.tsx
---------
Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
---
src/plugins/clickableRoles/index.tsx | 187 +++++++++++++++++++++++++++
src/plugins/clickableRoles/style.css | 80 ++++++++++++
2 files changed, 267 insertions(+)
create mode 100644 src/plugins/clickableRoles/index.tsx
create mode 100644 src/plugins/clickableRoles/style.css
diff --git a/src/plugins/clickableRoles/index.tsx b/src/plugins/clickableRoles/index.tsx
new file mode 100644
index 000000000..8f86916c0
--- /dev/null
+++ b/src/plugins/clickableRoles/index.tsx
@@ -0,0 +1,187 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2026 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import "./style.css";
+
+import ErrorBoundary from "@components/ErrorBoundary";
+import { Devs } from "@utils/constants";
+import { classNameFactory } from "@utils/css";
+import { openUserProfile } from "@utils/discord";
+import { isTruthy } from "@utils/guards";
+import { Logger } from "@utils/Logger";
+import definePlugin from "@utils/types";
+import type { Role } from "@vencord/discord-types";
+import { findByPropsLazy } from "@webpack";
+import { Constants, GuildRoleStore, IconUtils, Popout, RestAPI, ScrollerThin, useEffect, useRef, UserStore, useState, useStateFromStores } from "@webpack/common";
+
+const logger = new Logger("ClickableRoles");
+
+const cl = classNameFactory("vc-clickableroles-");
+
+const GuildActions = findByPropsLazy("requestMembersById", "banUser");
+
+const MAX_VISIBLE_MEMBERS = 20;
+const CACHE_TTL = 60_000;
+
+const memberCache = new Map();
+
+function getCachedMemberIds(guildId: string, roleId: string) {
+ const key = `${guildId}-${roleId}`;
+ const cached = memberCache.get(key);
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL) return cached.ids;
+ return null;
+}
+
+function setCachedMemberIds(guildId: string, roleId: string, ids: string[]) {
+ memberCache.set(`${guildId}-${roleId}`, { ids, timestamp: Date.now() });
+}
+
+function RoleMembersList({ roleId, guildId, closePopout, setPopoutRef }: { roleId: string; guildId: string; closePopout(): void; setPopoutRef(ref: HTMLDivElement | null): void; }) {
+ const [memberIds, setMemberIds] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ const role = useStateFromStores([GuildRoleStore], () => GuildRoleStore.getRole(guildId, roleId));
+
+ const [totalCount, setTotalCount] = useState(0);
+
+ useEffect(() => {
+ let cancelled = false;
+
+ const cached = getCachedMemberIds(guildId, roleId);
+ if (cached) {
+ setTotalCount(cached.length);
+ const visible = cached.slice(0, MAX_VISIBLE_MEMBERS);
+ setMemberIds(visible);
+ if (visible.length) GuildActions.requestMembersById(guildId, visible, false);
+ setLoading(false);
+ } else {
+ RestAPI.get({
+ url: Constants.Endpoints.GUILD_ROLE_MEMBER_IDS(guildId, roleId),
+ }).then(res => {
+ if (cancelled) return;
+ const ids = res.body as string[];
+ setCachedMemberIds(guildId, roleId, ids);
+ setTotalCount(ids.length);
+ const visible = ids.slice(0, MAX_VISIBLE_MEMBERS);
+ setMemberIds(visible);
+ if (visible.length) GuildActions.requestMembersById(guildId, visible, false);
+ setLoading(false);
+ }).catch(e => {
+ if (cancelled) return;
+ logger.error("Failed to fetch role members", e);
+ setLoading(false);
+ });
+ }
+
+ return () => { cancelled = true; };
+ }, [guildId, roleId]);
+
+ const users = useStateFromStores(
+ [UserStore],
+ () => memberIds.map(id => UserStore.getUser(id)).filter(isTruthy),
+ [memberIds]
+ );
+
+ return (
+
+
+
+ {role?.name ?? "Unknown Role"}
+ {totalCount}
+
+
+ {loading ? (
+ Loading members...
+ ) : users.length === 0 ? (
+ No members found.
+ ) : users.map(user => (
+ {
+ closePopout();
+ openUserProfile(user.id);
+ }}
+ >
+
})
+
{user.globalName ?? user.username}
+
+ ))}
+ {totalCount > MAX_VISIBLE_MEMBERS && (
+ and {totalCount - MAX_VISIBLE_MEMBERS} more
+ )}
+
+
+ );
+}
+
+function ClickableRole({ roleId, guildId, children }: { roleId: string; guildId: string; children: React.ReactNode; }) {
+ const ref = useRef(null);
+
+ return (
+ (
+
+ )}
+ >
+ {popoutProps => (
+
+ {children}
+
+ )}
+
+ );
+}
+
+const WrappedClickableRole = ErrorBoundary.wrap(ClickableRole, { noop: true });
+
+export default definePlugin({
+ name: "ClickableRoles",
+ description: "Click on roles in user profiles and the member list to see which members have them.",
+ authors: [Devs.prism],
+
+ patches: [
+ {
+ find: "#{intl::T+3Adg::raw}",
+ replacement: {
+ match: /(colors:\i\}\);)return(.*?enableTooltip:!1\}\):null,\i\]\}\))/,
+ replace: "$1return $self.wrapRolePill(arguments[0],()=>$2)",
+ },
+ },
+ {
+ find: 'tutorialId:"whos-online"',
+ replacement: {
+ match: /\((function\(\i\)\{let\{id:.*?"\\xa0โ ".{0,15})\}\);/,
+ replace: "($self.wrapRoleGroup($1}));",
+ },
+ },
+ ],
+
+ wrapRoleGroup(originalFn: (props: { id: string; guildId: string; }) => React.ReactNode) {
+ return (props: { id: string; guildId: string; }) => {
+ const result = originalFn(props);
+ if (!GuildRoleStore.getRole(props.guildId, props.id)) return result;
+ return (
+
+ {result}
+
+ );
+ };
+ },
+
+ wrapRolePill(props: { role: Role; guildId: string; }, renderOriginal: () => React.ReactNode) {
+ return (
+
+ {renderOriginal()}
+
+ );
+ },
+});
diff --git a/src/plugins/clickableRoles/style.css b/src/plugins/clickableRoles/style.css
new file mode 100644
index 000000000..59a1ce523
--- /dev/null
+++ b/src/plugins/clickableRoles/style.css
@@ -0,0 +1,80 @@
+.vc-clickableroles-trigger {
+ cursor: pointer;
+}
+
+.vc-clickableroles-popout {
+ background-color: var(--background-surface-highest);
+ border-radius: 8px;
+ padding: 12px;
+ width: 250px;
+ max-height: var(--reference-position-layer-max-height, 350px);
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+}
+
+.vc-clickableroles-list {
+ max-height: 260px;
+ min-height: 0;
+ flex: 1;
+}
+
+.vc-clickableroles-header {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ margin-bottom: 8px;
+}
+
+.vc-clickableroles-color {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+
+.vc-clickableroles-name {
+ font-weight: 600;
+ color: var(--interactive-text-default);
+}
+
+.vc-clickableroles-count {
+ color: var(--text-muted);
+ margin-left: auto;
+ font-size: 12px;
+}
+
+.vc-clickableroles-empty {
+ padding: 8px;
+ text-align: center;
+ color: var(--text-muted);
+ font-size: 13px;
+}
+
+.vc-clickableroles-user {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 4px 6px;
+ cursor: pointer;
+ border-radius: 4px;
+ color: var(--interactive-text-default);
+ font-size: 14px;
+}
+
+.vc-clickableroles-user:hover {
+ background-color: var(--interactive-background-hover);
+}
+
+.vc-clickableroles-avatar {
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+}
+
+.vc-clickableroles-overflow {
+ padding: 6px;
+ text-align: center;
+ color: var(--text-muted);
+ font-size: 12px;
+}
From aea2ae724f560b83e414652b55eacbecd1a8a21f Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Tue, 3 Mar 2026 11:25:05 -0500
Subject: [PATCH 05/41] move clickableroles
---
src/{plugins => equicordplugins}/clickableRoles/index.tsx | 0
src/{plugins => equicordplugins}/clickableRoles/style.css | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename src/{plugins => equicordplugins}/clickableRoles/index.tsx (100%)
rename src/{plugins => equicordplugins}/clickableRoles/style.css (100%)
diff --git a/src/plugins/clickableRoles/index.tsx b/src/equicordplugins/clickableRoles/index.tsx
similarity index 100%
rename from src/plugins/clickableRoles/index.tsx
rename to src/equicordplugins/clickableRoles/index.tsx
diff --git a/src/plugins/clickableRoles/style.css b/src/equicordplugins/clickableRoles/style.css
similarity index 100%
rename from src/plugins/clickableRoles/style.css
rename to src/equicordplugins/clickableRoles/style.css
From 4361d0c089ebe681bcca07fa4f70c4f3ee1bf08b Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Tue, 3 Mar 2026 11:28:43 -0500
Subject: [PATCH 06/41] I have no idea if this actually fixes the patch because
it works for me fine
---
src/equicordplugins/clickableRoles/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/equicordplugins/clickableRoles/index.tsx b/src/equicordplugins/clickableRoles/index.tsx
index 8f86916c0..0447a7d26 100644
--- a/src/equicordplugins/clickableRoles/index.tsx
+++ b/src/equicordplugins/clickableRoles/index.tsx
@@ -159,7 +159,7 @@ export default definePlugin({
{
find: 'tutorialId:"whos-online"',
replacement: {
- match: /\((function\(\i\)\{let\{id:.*?"\\xa0โ ".{0,15})\}\);/,
+ match: /\((function\(\i\)\{let\{id:.*?xa0โ.{0,15})\}\);/,
replace: "($self.wrapRoleGroup($1}));",
},
},
From fd862598d46974236a00588798be1fdf2e8c2332 Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Tue, 3 Mar 2026 11:34:12 -0500
Subject: [PATCH 07/41] Fix Components Icon
---
src/components/Icons.tsx | 20 ++++++++++++++++
src/equicordplugins/clickableRoles/index.tsx | 2 +-
src/equicordplugins/components.dev/index.tsx | 25 +-------------------
3 files changed, 22 insertions(+), 25 deletions(-)
diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx
index 332cd3ecd..835ab7373 100644
--- a/src/components/Icons.tsx
+++ b/src/components/Icons.tsx
@@ -749,3 +749,23 @@ export const QrCodeIcon = (props?: any) => {
);
};
+
+export const ComponentsIcon = (props?: any) => {
+ return (
+
+
+
+
+ );
+};
diff --git a/src/equicordplugins/clickableRoles/index.tsx b/src/equicordplugins/clickableRoles/index.tsx
index 0447a7d26..6f4005e58 100644
--- a/src/equicordplugins/clickableRoles/index.tsx
+++ b/src/equicordplugins/clickableRoles/index.tsx
@@ -159,7 +159,7 @@ export default definePlugin({
{
find: 'tutorialId:"whos-online"',
replacement: {
- match: /\((function\(\i\)\{let\{id:.*?xa0โ.{0,15})\}\);/,
+ match: /\((function\(\i\)\{let\{id:.*?#{intl::CHANNEL_MEMBERS_A11Y_LABEL}.*?\}\))\}\);/,
replace: "($self.wrapRoleGroup($1}));",
},
},
diff --git a/src/equicordplugins/components.dev/index.tsx b/src/equicordplugins/components.dev/index.tsx
index 68f05fb63..a8269b4bc 100644
--- a/src/equicordplugins/components.dev/index.tsx
+++ b/src/equicordplugins/components.dev/index.tsx
@@ -6,6 +6,7 @@
import "./styles.css";
+import { ComponentsIcon } from "@components/Icons";
import SettingsPlugin from "@plugins/_core/settings";
import { Devs } from "@utils/constants";
import { removeFromArray } from "@utils/misc";
@@ -14,30 +15,6 @@ import { SettingsRouter } from "@webpack/common";
import ComponentsTab from "./components/ComponentsTab";
-function ComponentsIcon(props: React.SVGProps) {
- return (
-
- );
-}
-
export default definePlugin({
name: "Components",
description: "Adds a new tab to settings to browse Discord components.",
From 281aedcf1f0416cd8acb236b9e17c77e96568d33 Mon Sep 17 00:00:00 2001
From: M <91066601+mishl-dev@users.noreply.github.com>
Date: Tue, 3 Mar 2026 23:53:40 +0600
Subject: [PATCH 08/41] fix(jumpTo): add missing newline at end of file (#867)
---
src/equicordplugins/jumpTo/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/equicordplugins/jumpTo/index.tsx b/src/equicordplugins/jumpTo/index.tsx
index 36bfb3b7a..537d9c19f 100644
--- a/src/equicordplugins/jumpTo/index.tsx
+++ b/src/equicordplugins/jumpTo/index.tsx
@@ -129,4 +129,4 @@ export default definePlugin({
"user-context": UserMenuPatch,
"message": MessageMenuPatch
}
-});
\ No newline at end of file
+});
From 5fe5bd62a38d6f865adf92f95ea7969e0ab73a03 Mon Sep 17 00:00:00 2001
From: YashRaj <91825864+YashRajCodes@users.noreply.github.com>
Date: Tue, 3 Mar 2026 23:23:56 +0530
Subject: [PATCH 09/41] ci: make ci faster by not installing chrome (#865)
* ci: apparently ubuntu-latest has chrome preinstalled
* ci: refactor and prefer offline cached deps
---
.github/workflows/reportBrokenPlugins.yml | 16 +++-------------
1 file changed, 3 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/reportBrokenPlugins.yml b/.github/workflows/reportBrokenPlugins.yml
index b989f896f..117a9a70c 100644
--- a/.github/workflows/reportBrokenPlugins.yml
+++ b/.github/workflows/reportBrokenPlugins.yml
@@ -28,12 +28,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- if: ${{ github.event_name == 'schedule' }}
with:
- ref: dev
-
- - uses: actions/checkout@v4
- if: ${{ github.event_name == 'workflow_dispatch' }}
+ ref: ${{ github.event_name == 'schedule' && 'dev' || github.ref }}
- uses: pnpm/action-setup@v3 # Install pnpm using packageManager key in package.json
@@ -45,13 +41,7 @@ jobs:
- name: Install dependencies
run: |
- pnpm install --frozen-lockfile
-
- - name: Install Google Chrome
- id: setup-chrome
- uses: browser-actions/setup-chrome@82b9ce628cc5595478a9ebadc480958a36457dc2
- with:
- chrome-version: stable
+ pnpm install --frozen-lockfile --prefer-offline
- name: Build Equicord Reporter Version
run: pnpm buildReporter
@@ -60,7 +50,7 @@ jobs:
timeout-minutes: 10
run: |
export PATH="$PWD/node_modules/.bin:$PATH"
- export CHROMIUM_BIN=${{ steps.setup-chrome.outputs.chrome-path }}
+ export CHROMIUM_BIN=/usr/bin/google-chrome
esbuild scripts/generateReport.ts > dist/report.mjs
From 7a546afa1f88116cbddc182b78a4385c153dffb5 Mon Sep 17 00:00:00 2001
From: nexpid
Date: Tue, 3 Mar 2026 21:18:20 +0100
Subject: [PATCH 10/41] SongSpotlight: UI updates (#868)
* feat(SongSpotlight): mostly UI updates!
- new compact settings
- css fixes
- & more!
* fix(SongSpotlight): dont really need the filter
* feat(SongSpotlight): update the wording
---
.../songSpotlight.desktop/index.tsx | 18 +--
.../songSpotlight.desktop/settings.tsx | 34 ++++++
.../songSpotlight.desktop/style.css | 23 ++--
.../songSpotlight.desktop/ui/common.tsx | 8 ++
.../ui/songs/CollapsedProfileSongs.tsx | 103 ++++++++++++++++++
.../ui/songs/ProfileSongs.tsx | 68 ++++++++++--
.../songSpotlight.desktop/ui/songs/index.tsx | 2 +-
7 files changed, 223 insertions(+), 33 deletions(-)
create mode 100644 src/equicordplugins/songSpotlight.desktop/settings.tsx
create mode 100644 src/equicordplugins/songSpotlight.desktop/ui/songs/CollapsedProfileSongs.tsx
diff --git a/src/equicordplugins/songSpotlight.desktop/index.tsx b/src/equicordplugins/songSpotlight.desktop/index.tsx
index 83ecdf203..ed8d84c12 100644
--- a/src/equicordplugins/songSpotlight.desktop/index.tsx
+++ b/src/equicordplugins/songSpotlight.desktop/index.tsx
@@ -6,15 +6,14 @@
import "./style.css";
-import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { EquicordDevs } from "@utils/constants";
-import definePlugin, { OptionType } from "@utils/types";
+import definePlugin from "@utils/types";
import { useAuthorizationStore } from "./lib/stores/AuthorizationStore";
import { useSongStore } from "./lib/stores/SongStore";
import { Native } from "./service";
-import Settings from "./ui/settings";
+import settings from "./settings";
import ProfileSongs from "./ui/songs/ProfileSongs";
import WidgetSongs from "./ui/songs/WidgetSongs";
@@ -22,12 +21,7 @@ export default definePlugin({
name: "SongSpotlight",
description: "Show off songs on your profile",
authors: [EquicordDevs.nexpid],
- settings: definePluginSettings({
- manager: {
- type: OptionType.COMPONENT,
- component: () => ,
- },
- }),
+ settings,
patches: [
// Personal profile popout
{
@@ -50,7 +44,7 @@ export default definePlugin({
find: ".SIDEBAR}),nicknameIcons:",
replacement: {
match: /{userId:(\i)\.id}\)}\).{0,100}]}\)(?=\]\}\))/,
- replace: "$&,$self.renderProfileSongs({userId:$1.id})",
+ replace: "$&,$self.renderProfileSongs({userId:$1.id,isSidebar:true})",
},
},
// Full profile modal sections (lazy loaded)
@@ -58,14 +52,14 @@ export default definePlugin({
find: ".MUTUAL_GUILDS})),",
replacement: {
match: /(\i).push\({text.{0,50}}\);/,
- replace: "$&$1.push({text:\"Song Spotlight\",section:\"SONG_SPOTLIGHT\"});",
+ replace: '$&$1.push({text:"Song Spotlight",section:"SONG_SPOTLIGHT"});',
},
},
{
find: ".hasUnsavedChanges()&&",
replacement: {
match: /({user:(\i),.{0,80}return (\i===))/,
- replace: "$1\"SONG_SPOTLIGHT\"?$self.renderWidgetSongs({user:$2}):$3",
+ replace: '$1"SONG_SPOTLIGHT"?$self.renderWidgetSongs({user:$2}):$3',
},
},
],
diff --git a/src/equicordplugins/songSpotlight.desktop/settings.tsx b/src/equicordplugins/songSpotlight.desktop/settings.tsx
new file mode 100644
index 000000000..2819a7adb
--- /dev/null
+++ b/src/equicordplugins/songSpotlight.desktop/settings.tsx
@@ -0,0 +1,34 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2026 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { definePluginSettings } from "@api/Settings";
+import { makeRange, OptionType } from "@utils/types";
+
+import Settings from "./ui/settings";
+
+export default definePluginSettings({
+ collapseSongList: {
+ type: OptionType.BOOLEAN,
+ description: "Collapses the song list on user profiles to a button which opens a separate menu",
+ default: false,
+ },
+ profileSongsLimit: {
+ type: OptionType.SLIDER,
+ description: "How many songs are shown when initially clicking on a user",
+ default: 6,
+ markers: makeRange(1, 6),
+ },
+ manager: {
+ type: OptionType.COMPONENT,
+ component: () => ,
+ },
+}, {
+ profileSongsLimit: {
+ disabled() {
+ return this.store.collapseSongList;
+ },
+ },
+});
diff --git a/src/equicordplugins/songSpotlight.desktop/style.css b/src/equicordplugins/songSpotlight.desktop/style.css
index 42f8cb293..bb44f9aaa 100644
--- a/src/equicordplugins/songSpotlight.desktop/style.css
+++ b/src/equicordplugins/songSpotlight.desktop/style.css
@@ -23,7 +23,10 @@
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
+ min-width: 0;
+ max-width: 100%;
overflow: hidden;
+ text-overflow: ellipsis;
}
/* components */
@@ -137,13 +140,13 @@
--list-height: calc(var(--base-height) + 80px);
--number-size: 32px;
- padding: 8px;
- gap: 12px;
- overflow: visible;
-}
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
-.user-profile-sidebar .vc-songspotlight-songs-container {
- padding: 12px;
+ &.vc-songspotlight-songs-container-sidebar {
+ padding: 12px;
+ }
}
.vc-songspotlight-songs-more {
@@ -194,6 +197,10 @@
}
}
+.user-profile-sidebar .vc-songspotlight-song-container {
+ padding: 8px;
+}
+
.user-profile-popout:not(.custom-user-profile-theme) .vc-songspotlight-song-container {
background: var(--opacity-16);
}
@@ -211,7 +218,6 @@
grid-template-columns: 1fr auto;
grid-template-rows: 1fr auto;
gap: 8px;
- overflow: visible;
border-radius: inherit;
}
@@ -220,7 +226,7 @@
grid-column: 1;
grid-row: 1 / 3;
border-radius: inherit;
- overflow: visible;
+ min-width: 0;
}
.vc-songspotlight-song-cover {
@@ -232,7 +238,6 @@
flex-shrink: 0;
box-shadow: 0 3px 12px 0 rgb(0 0 0 / 16%);
transition: 0.18s transform;
- overflow: visible;
&:hover {
transform: scale(1.03);
diff --git a/src/equicordplugins/songSpotlight.desktop/ui/common.tsx b/src/equicordplugins/songSpotlight.desktop/ui/common.tsx
index 0e5454fad..eda340ce4 100644
--- a/src/equicordplugins/songSpotlight.desktop/ui/common.tsx
+++ b/src/equicordplugins/songSpotlight.desktop/ui/common.tsx
@@ -12,6 +12,14 @@ import { Tooltip } from "@webpack/common";
import { ComponentType, HTMLAttributes, JSX } from "react";
export const OverlayClasses = findCssClassesLazy("overlay", "inner", "outer");
+export const ContainerClasses = findCssClassesLazy(
+ "breadcrumb",
+ "innerContainer",
+ "icons",
+ "icon",
+ "displayCount",
+ "displayCountText",
+);
export const CardClasses = findCssClassesLazy("card", "headerText", "headerContextMenu");
export const WidgetClasses = findCssClassesLazy("tabPanelScroller", "tabListScroller");
diff --git a/src/equicordplugins/songSpotlight.desktop/ui/songs/CollapsedProfileSongs.tsx b/src/equicordplugins/songSpotlight.desktop/ui/songs/CollapsedProfileSongs.tsx
new file mode 100644
index 000000000..52fa56ab1
--- /dev/null
+++ b/src/equicordplugins/songSpotlight.desktop/ui/songs/CollapsedProfileSongs.tsx
@@ -0,0 +1,103 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2026 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { BaseText } from "@components/index";
+import { Native } from "@equicordplugins/songSpotlight.desktop/service";
+import { CardClasses, ContainerClasses, OverlayClasses, Spinner } from "@equicordplugins/songSpotlight.desktop/ui/common";
+import { RenderSongInfo } from "@song-spotlight/api/handlers";
+import { UserData } from "@song-spotlight/api/structs";
+import { sid } from "@song-spotlight/api/util";
+import { classes } from "@utils/index";
+import {
+ SelectedChannelStore,
+ SelectedGuildStore,
+ useEffect,
+ useMemo,
+ UserProfileActions,
+ UserStore,
+ useState,
+} from "@webpack/common";
+
+import { ProfileSongsProps } from "./ProfileSongs";
+
+const shownSongs = 4;
+
+interface CollapsedProfileSongsProps extends ProfileSongsProps {
+ data?: UserData;
+}
+
+export default function CollapsedProfileSongs({ data, userId, isSidebar }: CollapsedProfileSongsProps) {
+ const [renders, setRenders] = useState(new Map());
+ const previews = useMemo(() => data?.slice(0, shownSongs), [data]);
+
+ useEffect(() => {
+ setRenders(new Map());
+ if (!previews) return;
+
+ for (const song of previews) {
+ Native.renderSong(song)
+ .catch(() => null)
+ .then(info => {
+ if (!info) return;
+ setRenders(renders => new Map(renders).set(sid(song), info));
+ });
+ }
+ }, [previews]);
+
+ return (
+ {
+ const user = UserStore.getUser(userId);
+ if (!user) return;
+
+ const guildId = SelectedGuildStore.getGuildId();
+ UserProfileActions.openUserProfileModal({
+ userId,
+ guildId,
+ channelId: SelectedChannelStore.getChannelId(),
+ analyticsLocation: {
+ page: guildId ? "Guild Channel" : "DM Channel",
+ section: "Profile Popout",
+ },
+ tabSection: "SONG_SPOTLIGHT",
+ });
+ }}
+ >
+
+
Song Spotlight
+
+ {previews && data
+ ? previews.map((song, i) => {
+ const render = renders.get(sid(song));
+ const extra = i === shownSongs - 1 && data.length > shownSongs;
+
+ return (
+
+ {render?.thumbnailUrl && (
+

+ )}
+ {extra && (
+
+ +{data.length - shownSongs}
+
+ )}
+
+ );
+ })
+ :
}
+
+
+
+ );
+}
diff --git a/src/equicordplugins/songSpotlight.desktop/ui/songs/ProfileSongs.tsx b/src/equicordplugins/songSpotlight.desktop/ui/songs/ProfileSongs.tsx
index 9bc46c95f..8137349c4 100644
--- a/src/equicordplugins/songSpotlight.desktop/ui/songs/ProfileSongs.tsx
+++ b/src/equicordplugins/songSpotlight.desktop/ui/songs/ProfileSongs.tsx
@@ -7,10 +7,12 @@
import { BaseText } from "@components/BaseText";
import { Flex } from "@components/Flex";
import { LinkIcon, PencilIcon } from "@components/Icons";
+import { Link } from "@components/Link";
import { listData } from "@equicordplugins/songSpotlight.desktop/lib/api";
import { useAuthorizationStore } from "@equicordplugins/songSpotlight.desktop/lib/stores/AuthorizationStore";
import { useSongStore } from "@equicordplugins/songSpotlight.desktop/lib/stores/SongStore";
import { cl } from "@equicordplugins/songSpotlight.desktop/lib/utils";
+import settings from "@equicordplugins/songSpotlight.desktop/settings";
import {
CardClasses,
MoreHorizontalIcon,
@@ -20,40 +22,75 @@ import {
import { openSettingsModal } from "@equicordplugins/songSpotlight.desktop/ui/settings";
import { sid } from "@song-spotlight/api/util";
import { copyWithToast } from "@utils/discord";
-import { classes } from "@utils/misc";
-import { ContextMenuApi, FluxDispatcher, Menu, React, Tooltip, useEffect, UserStore, useState } from "@webpack/common";
+import { classes } from "@utils/index";
+import {
+ ContextMenuApi,
+ FluxDispatcher,
+ Menu,
+ React,
+ Tooltip,
+ useEffect,
+ useMemo,
+ UserStore,
+ useState,
+} from "@webpack/common";
import Song from ".";
+import CollapsedProfileSongs from "./CollapsedProfileSongs";
-interface ProfileSongsProps {
+export interface ProfileSongsProps {
userId: string;
+ isSidebar: boolean;
}
-export default function ProfileSongs({ userId }: ProfileSongsProps) {
+export default function ProfileSongs({ userId, isSidebar }: ProfileSongsProps) {
const [failed, setFailed] = useState(false);
const { isAuthorized } = useAuthorizationStore();
const { users } = useSongStore();
+ const { profileSongsLimit, collapseSongList } = settings.use();
- const data = users[userId]?.data, at = users[userId]?.at;
+ const data = users[userId]?.data;
useEffect(() => {
if (isAuthorized() && !data) listData(userId).catch(() => setFailed(true));
}, [isAuthorized()]);
+ const [clamped, setClamped] = useState(true);
+ const clampedData = useMemo(() => data && (clamped ? data.slice(0, profileSongsLimit) : data), [
+ data,
+ clamped,
+ profileSongsLimit,
+ ]);
+
const owned = UserStore.getCurrentUser().id === userId;
- if (isAuthorized() && !data && !failed) {
+ const pending = isAuthorized() && !clampedData && !data && !failed;
+ if (!pending && !clampedData?.[0]) return null;
+
+ if (collapseSongList) {
+ return (
+
+ );
+ } else if (pending) {
return ;
- } else if (!data?.[0]) {
- return null;
}
return (
- Song Spotlight
+
+ Song Spotlight
+
{props => (
- {data.map((song, i) => )}
+ {clampedData.map((song, i) => )}
+ {clamped && data.length > profileSongsLimit && (
+ setClamped(false)}
+ >
+
+ Show {data.length - profileSongsLimit} more
+
+
+ )}
);
}
diff --git a/src/equicordplugins/songSpotlight.desktop/ui/songs/index.tsx b/src/equicordplugins/songSpotlight.desktop/ui/songs/index.tsx
index ee42b87d6..a1fd51434 100644
--- a/src/equicordplugins/songSpotlight.desktop/ui/songs/index.tsx
+++ b/src/equicordplugins/songSpotlight.desktop/ui/songs/index.tsx
@@ -187,7 +187,7 @@ function SongInfo({ owned, song, render, big }: SongInfoProps) {
icon={LinkIcon}
action={() => copyWithToast(render.link)}
/>
- {owned
+ {!owned
? (
Date: Tue, 3 Mar 2026 18:42:42 -0500
Subject: [PATCH 11/41] Declutter: remove avatar decoration setting
---
src/equicordplugins/declutter/index.tsx | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/src/equicordplugins/declutter/index.tsx b/src/equicordplugins/declutter/index.tsx
index 661d94fe6..a88a0fcd3 100644
--- a/src/equicordplugins/declutter/index.tsx
+++ b/src/equicordplugins/declutter/index.tsx
@@ -36,12 +36,6 @@ export const settings = definePluginSettings({
type: OptionType.COMPONENT,
component: () => SectionSeparator("User Profile"),
},
- removeAvatarDecoration: {
- type: OptionType.BOOLEAN,
- description: "Remove avatar decorations.",
- default: true,
- restartNeeded: true,
- },
removeNameplate: {
type: OptionType.BOOLEAN,
description: "Remove nameplates.",
@@ -149,15 +143,6 @@ export default definePlugin({
authors: [EquicordDevs.Leon135, Devs.prism, Devs.Kyuuhachi],
settings,
patches: [
- {
- // Avatar decoration
- find: "getAvatarDecorationURL:",
- replacement: {
- match: /(?<=function \i\((\i)\){)(?=.{0,150}let{avatarDecoration)/,
- replace: "$&return null;"
- },
- predicate: () => settings.store.removeAvatarDecoration,
- },
{
// Nameplate
find: "#{intl::AVATAR_MALLOW}",
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 12/41] 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 13/41] 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 14/41] 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 15/41] 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 16/41] 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 17/41] 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 18/41] 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 19/41] 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 20/41] 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 22/41] 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 23/41] 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 24/41] 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 25/41] 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 26/41] 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 27/41] 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 28/41] 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 29/41] 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 30/41] 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 31/41] 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 32/41] 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 33/41] 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 34/41] 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 35/41] 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 36/41] 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 37/41] 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 38/41] 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 39/41] 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 40/41] 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 (
From 6ad06e6091a4d9813c61038bd8bd6282be9adf89 Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Fri, 6 Mar 2026 21:10:20 -0500
Subject: [PATCH 41/41] Fix OAuth2AuthorizeModal Find
---
src/webpack/common/components.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts
index e830e52ef..2a1ca4efb 100644
--- a/src/webpack/common/components.ts
+++ b/src/webpack/common/components.ts
@@ -112,7 +112,7 @@ waitFor(m => {
export const MaskedLink = waitForComponent("MaskedLink", filters.componentByCode("MASKED_LINK)"));
export const Timestamp = waitForComponent("Timestamp", filters.componentByCode("#{intl::MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL}"));
-export const OAuth2AuthorizeModal = waitForComponent("OAuth2AuthorizeModal", filters.componentByCode("hasContentBackground", "oauth2_authorize"));
+export const OAuth2AuthorizeModal = waitForComponent("OAuth2AuthorizeModal", filters.componentByCode("hasContentBackground", "nextStep", "onClose?.()"));
export const Animations = mapMangledModuleLazy(".assign({colorNames:", {
Transition: filters.componentByCode('["items","children"]', ",null,"),