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} -
- +
+ + + {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 && ( + {render.label} + )} + {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,"),