require("../../plugins"));
@@ -69,7 +69,7 @@ function ReloadRequiredCard({ required }: { required: boolean; }) {
Restart now to apply new plugins and their settings
-
,
- onCloseCallback: () => setTimeout(() => NavigationRouter.back(), 50)
+
});
}
@@ -163,8 +217,7 @@ ${makeCodeblock(enabledPlugins.join(", "))}
Please either switch to an officially supported version of Vencord, or
contact your package maintainer for support instead.
- ,
- onCloseCallback: () => setTimeout(() => NavigationRouter.back(), 50)
+
});
}
}
@@ -172,7 +225,7 @@ ${makeCodeblock(enabledPlugins.join(", "))}
ContributorDmWarningCard: ErrorBoundary.wrap(({ userId }) => {
if (!isPluginDev(userId)) return null;
- if (RelationshipStore.isFriend(userId)) return null;
+ if (RelationshipStore.isFriend(userId) || isPluginDev(UserStore.getCurrentUser()?.id)) return null;
return (
@@ -182,5 +235,86 @@ ${makeCodeblock(enabledPlugins.join(", "))}
{!ChannelStore.getChannel(SUPPORT_CHANNEL_ID) && " (Click the link to join)"}
);
- }, { noop: true })
+ }, { noop: true }),
+
+ start() {
+ addAccessory("vencord-debug", props => {
+ const buttons = [] as JSX.Element[];
+
+ const shouldAddUpdateButton =
+ !IS_UPDATER_DISABLED
+ && (
+ (props.channel.id === KNOWN_ISSUES_CHANNEL_ID) ||
+ (props.channel.id === SUPPORT_CHANNEL_ID && props.message.author.id === VENBOT_USER_ID)
+ )
+ && props.message.content?.includes("update");
+
+ if (shouldAddUpdateButton) {
+ buttons.push(
+ {
+ try {
+ if (await forceUpdate())
+ showToast("Success! Restarting...", Toasts.Type.SUCCESS);
+ else
+ showToast("Already up to date!", Toasts.Type.MESSAGE);
+ } catch (e) {
+ new Logger(this.name).error("Error while updating:", e);
+ showToast("Failed to update :(", Toasts.Type.FAILURE);
+ }
+ }}
+ >
+ Update Now
+
+ );
+ }
+
+ if (props.channel.id === SUPPORT_CHANNEL_ID) {
+ if (props.message.content.includes("/vencord-debug") || props.message.content.includes("/vencord-plugins")) {
+ buttons.push(
+ sendMessage(props.channel.id, { content: await generateDebugInfoMessage() })}
+ >
+ Run /vencord-debug
+ ,
+ sendMessage(props.channel.id, { content: generatePluginList() })}
+ >
+ Run /vencord-plugins
+
+ );
+ }
+
+ if (props.message.author.id === VENBOT_USER_ID) {
+ const match = CodeBlockRe.exec(props.message.content || props.message.embeds[0]?.rawDescription || "");
+ if (match) {
+ buttons.push(
+ {
+ try {
+ await AsyncFunction(match[1])();
+ showToast("Success!", Toasts.Type.SUCCESS);
+ } catch (e) {
+ new Logger(this.name).error("Error while running snippet:", e);
+ showToast("Failed to run snippet :(", Toasts.Type.FAILURE);
+ }
+ }}
+ >
+ Run Snippet
+
+ );
+ }
+ }
+ }
+
+ return buttons.length
+ ? {buttons}
+ : null;
+ });
+ },
});
diff --git a/src/plugins/alwaysAnimate/index.ts b/src/plugins/alwaysAnimate/index.ts
index dbec3b4e3..20cb4f974 100644
--- a/src/plugins/alwaysAnimate/index.ts
+++ b/src/plugins/alwaysAnimate/index.ts
@@ -31,10 +31,10 @@ export default definePlugin({
// Some modules match the find but the replacement is returned untouched
noWarn: true,
replacement: {
- match: /canAnimate:.+?(?=([,}].*?\)))/g,
+ match: /canAnimate:.+?([,}].*?\))/g,
replace: (m, rest) => {
const destructuringMatch = rest.match(/}=.+/);
- if (destructuringMatch == null) return "canAnimate:!0";
+ if (destructuringMatch == null) return `canAnimate:!0${rest}`;
return m;
}
}
diff --git a/src/plugins/alwaysTrust/index.ts b/src/plugins/alwaysTrust/index.ts
index b195e8ebf..7484a619c 100644
--- a/src/plugins/alwaysTrust/index.ts
+++ b/src/plugins/alwaysTrust/index.ts
@@ -49,7 +49,7 @@ export default definePlugin({
predicate: () => settings.store.domain
},
{
- find: "isSuspiciousDownload:",
+ find: "bitbucket.org",
replacement: {
match: /function \i\(\i\){(?=.{0,60}\.parse\(\i\))/,
replace: "$&return null;"
diff --git a/src/plugins/appleMusic.desktop/README.md b/src/plugins/appleMusic.desktop/README.md
new file mode 100644
index 000000000..52ab93bfd
--- /dev/null
+++ b/src/plugins/appleMusic.desktop/README.md
@@ -0,0 +1,9 @@
+# AppleMusicRichPresence
+
+This plugin enables Discord rich presence for your Apple Music! (This only works on macOS with the Music app.)
+
+
+
+## Configuration
+
+For the customizable activity format strings, you can use several special strings to include track data in activities! `{name}` is replaced with the track name; `{artist}` is replaced with the artist(s)' name(s); and `{album}` is replaced with the album name.
diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx
new file mode 100644
index 000000000..6fa989cdd
--- /dev/null
+++ b/src/plugins/appleMusic.desktop/index.tsx
@@ -0,0 +1,262 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { definePluginSettings } from "@api/Settings";
+import { Devs } from "@utils/constants";
+import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
+import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
+
+const Native = VencordNative.pluginHelpers.AppleMusicRichPresence as PluginNative;
+
+interface ActivityAssets {
+ large_image?: string;
+ large_text?: string;
+ small_image?: string;
+ small_text?: string;
+}
+
+interface ActivityButton {
+ label: string;
+ url: string;
+}
+
+interface Activity {
+ state: string;
+ details?: string;
+ timestamps?: {
+ start?: number;
+ end?: number;
+ };
+ assets?: ActivityAssets;
+ buttons?: Array;
+ name: string;
+ application_id: string;
+ metadata?: {
+ button_urls?: Array;
+ };
+ type: number;
+ flags: number;
+}
+
+const enum ActivityType {
+ PLAYING = 0,
+ LISTENING = 2,
+}
+
+const enum ActivityFlag {
+ INSTANCE = 1 << 0,
+}
+
+export interface TrackData {
+ name: string;
+ album: string;
+ artist: string;
+
+ appleMusicLink?: string;
+ songLink?: string;
+
+ albumArtwork?: string;
+ artistArtwork?: string;
+
+ playerPosition: number;
+ duration: number;
+}
+
+const enum AssetImageType {
+ Album = "Album",
+ Artist = "Artist",
+ Disabled = "Disabled"
+}
+
+const applicationId = "1239490006054207550";
+
+function setActivity(activity: Activity | null) {
+ FluxDispatcher.dispatch({
+ type: "LOCAL_ACTIVITY_UPDATE",
+ activity,
+ socketId: "AppleMusic",
+ });
+}
+
+const settings = definePluginSettings({
+ activityType: {
+ type: OptionType.SELECT,
+ description: "Which type of activity",
+ options: [
+ { label: "Playing", value: ActivityType.PLAYING, default: true },
+ { label: "Listening", value: ActivityType.LISTENING }
+ ],
+ },
+ refreshInterval: {
+ type: OptionType.SLIDER,
+ description: "The interval between activity refreshes (seconds)",
+ markers: [1, 2, 2.5, 3, 5, 10, 15],
+ default: 5,
+ restartNeeded: true,
+ },
+ enableTimestamps: {
+ type: OptionType.BOOLEAN,
+ description: "Whether or not to enable timestamps",
+ default: true,
+ },
+ enableButtons: {
+ type: OptionType.BOOLEAN,
+ description: "Whether or not to enable buttons",
+ default: true,
+ },
+ nameString: {
+ type: OptionType.STRING,
+ description: "Activity name format string",
+ default: "Apple Music"
+ },
+ detailsString: {
+ type: OptionType.STRING,
+ description: "Activity details format string",
+ default: "{name}"
+ },
+ stateString: {
+ type: OptionType.STRING,
+ description: "Activity state format string",
+ default: "{artist}"
+ },
+ largeImageType: {
+ type: OptionType.SELECT,
+ description: "Activity assets large image type",
+ options: [
+ { label: "Album artwork", value: AssetImageType.Album, default: true },
+ { label: "Artist artwork", value: AssetImageType.Artist },
+ { label: "Disabled", value: AssetImageType.Disabled }
+ ],
+ },
+ largeTextString: {
+ type: OptionType.STRING,
+ description: "Activity assets large text format string",
+ default: "{album}"
+ },
+ smallImageType: {
+ type: OptionType.SELECT,
+ description: "Activity assets small image type",
+ options: [
+ { label: "Album artwork", value: AssetImageType.Album },
+ { label: "Artist artwork", value: AssetImageType.Artist, default: true },
+ { label: "Disabled", value: AssetImageType.Disabled }
+ ],
+ },
+ smallTextString: {
+ type: OptionType.STRING,
+ description: "Activity assets small text format string",
+ default: "{artist}"
+ },
+});
+
+function customFormat(formatStr: string, data: TrackData) {
+ return formatStr
+ .replaceAll("{name}", data.name)
+ .replaceAll("{album}", data.album)
+ .replaceAll("{artist}", data.artist);
+}
+
+function getImageAsset(type: AssetImageType, data: TrackData) {
+ const source = type === AssetImageType.Album
+ ? data.albumArtwork
+ : data.artistArtwork;
+
+ if (!source) return undefined;
+
+ return ApplicationAssetUtils.fetchAssetIds(applicationId, [source]).then(ids => ids[0]);
+}
+
+export default definePlugin({
+ name: "AppleMusicRichPresence",
+ description: "Discord rich presence for your Apple Music!",
+ authors: [Devs.RyanCaoDev],
+ hidden: !navigator.platform.startsWith("Mac"),
+ reporterTestable: ReporterTestable.None,
+
+ settingsAboutComponent() {
+ return <>
+
+ For the customizable activity format strings, you can use several special strings to include track data in activities!{" "}
+ {"{name}"} is replaced with the track name; {"{artist}"} is replaced with the artist(s)' name(s); and {"{album}"} is replaced with the album name.
+
+ >;
+ },
+
+ settings,
+
+ start() {
+ this.updatePresence();
+ this.updateInterval = setInterval(() => { this.updatePresence(); }, settings.store.refreshInterval * 1000);
+ },
+
+ stop() {
+ clearInterval(this.updateInterval);
+ FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity: null });
+ },
+
+ updatePresence() {
+ this.getActivity().then(activity => { setActivity(activity); });
+ },
+
+ async getActivity(): Promise {
+ const trackData = await Native.fetchTrackData();
+ if (!trackData) return null;
+
+ const [largeImageAsset, smallImageAsset] = await Promise.all([
+ getImageAsset(settings.store.largeImageType, trackData),
+ getImageAsset(settings.store.smallImageType, trackData)
+ ]);
+
+ const assets: ActivityAssets = {};
+
+ if (settings.store.largeImageType !== AssetImageType.Disabled) {
+ assets.large_image = largeImageAsset;
+ assets.large_text = customFormat(settings.store.largeTextString, trackData);
+ }
+
+ if (settings.store.smallImageType !== AssetImageType.Disabled) {
+ assets.small_image = smallImageAsset;
+ assets.small_text = customFormat(settings.store.smallTextString, trackData);
+ }
+
+ const buttons: ActivityButton[] = [];
+
+ if (settings.store.enableButtons) {
+ if (trackData.appleMusicLink)
+ buttons.push({
+ label: "Listen on Apple Music",
+ url: trackData.appleMusicLink,
+ });
+
+ if (trackData.songLink)
+ buttons.push({
+ label: "View on SongLink",
+ url: trackData.songLink,
+ });
+ }
+
+ return {
+ application_id: applicationId,
+
+ name: customFormat(settings.store.nameString, trackData),
+ details: customFormat(settings.store.detailsString, trackData),
+ state: customFormat(settings.store.stateString, trackData),
+
+ timestamps: (settings.store.enableTimestamps ? {
+ start: Date.now() - (trackData.playerPosition * 1000),
+ end: Date.now() - (trackData.playerPosition * 1000) + (trackData.duration * 1000),
+ } : undefined),
+
+ assets,
+
+ buttons: buttons.length ? buttons.map(v => v.label) : undefined,
+ metadata: { button_urls: buttons.map(v => v.url) || undefined, },
+
+ type: settings.store.activityType,
+ flags: ActivityFlag.INSTANCE,
+ };
+ }
+});
diff --git a/src/plugins/appleMusic.desktop/native.ts b/src/plugins/appleMusic.desktop/native.ts
new file mode 100644
index 000000000..2eb2a0757
--- /dev/null
+++ b/src/plugins/appleMusic.desktop/native.ts
@@ -0,0 +1,120 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { execFile } from "child_process";
+import { promisify } from "util";
+
+import type { TrackData } from ".";
+
+const exec = promisify(execFile);
+
+// function exec(file: string, args: string[] = []) {
+// return new Promise<{ code: number | null, stdout: string | null, stderr: string | null; }>((resolve, reject) => {
+// const process = spawn(file, args, { stdio: [null, "pipe", "pipe"] });
+
+// let stdout: string | null = null;
+// process.stdout.on("data", (chunk: string) => { stdout ??= ""; stdout += chunk; });
+// let stderr: string | null = null;
+// process.stderr.on("data", (chunk: string) => { stdout ??= ""; stderr += chunk; });
+
+// process.on("exit", code => { resolve({ code, stdout, stderr }); });
+// process.on("error", err => reject(err));
+// });
+// }
+
+async function applescript(cmds: string[]) {
+ const { stdout } = await exec("osascript", cmds.map(c => ["-e", c]).flat());
+ return stdout;
+}
+
+function makeSearchUrl(type: string, query: string) {
+ const url = new URL("https://tools.applemediaservices.com/api/apple-media/music/US/search.json");
+ url.searchParams.set("types", type);
+ url.searchParams.set("limit", "1");
+ url.searchParams.set("term", query);
+ return url;
+}
+
+const requestOptions: RequestInit = {
+ headers: { "user-agent": "Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0" },
+};
+
+interface RemoteData {
+ appleMusicLink?: string,
+ songLink?: string,
+ albumArtwork?: string,
+ artistArtwork?: string;
+}
+
+let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null;
+
+async function fetchRemoteData({ id, name, artist, album }: { id: string, name: string, artist: string, album: string; }) {
+ if (id === cachedRemoteData?.id) {
+ if ("data" in cachedRemoteData) return cachedRemoteData.data;
+ if ("failures" in cachedRemoteData && cachedRemoteData.failures >= 5) return null;
+ }
+
+ try {
+ const [songData, artistData] = await Promise.all([
+ fetch(makeSearchUrl("songs", artist + " " + album + " " + name), requestOptions).then(r => r.json()),
+ fetch(makeSearchUrl("artists", artist.split(/ *[,&] */)[0]), requestOptions).then(r => r.json())
+ ]);
+
+ const appleMusicLink = songData?.songs?.data[0]?.attributes.url;
+ const songLink = songData?.songs?.data[0]?.id ? `https://song.link/i/${songData?.songs?.data[0]?.id}` : undefined;
+
+ const albumArtwork = songData?.songs?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512");
+ const artistArtwork = artistData?.artists?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512");
+
+ cachedRemoteData = {
+ id,
+ data: { appleMusicLink, songLink, albumArtwork, artistArtwork }
+ };
+ return cachedRemoteData.data;
+ } catch (e) {
+ console.error("[AppleMusicRichPresence] Failed to fetch remote data:", e);
+ cachedRemoteData = {
+ id,
+ failures: (id === cachedRemoteData?.id && "failures" in cachedRemoteData ? cachedRemoteData.failures : 0) + 1
+ };
+ return null;
+ }
+}
+
+export async function fetchTrackData(): Promise {
+ try {
+ await exec("pgrep", ["^Music$"]);
+ } catch (error) {
+ return null;
+ }
+
+ const playerState = await applescript(['tell application "Music"', "get player state", "end tell"])
+ .then(out => out.trim());
+ if (playerState !== "playing") return null;
+
+ const playerPosition = await applescript(['tell application "Music"', "get player position", "end tell"])
+ .then(text => Number.parseFloat(text.trim()));
+
+ const stdout = await applescript([
+ 'set output to ""',
+ 'tell application "Music"',
+ "set t_id to database id of current track",
+ "set t_name to name of current track",
+ "set t_album to album of current track",
+ "set t_artist to artist of current track",
+ "set t_duration to duration of current track",
+ 'set output to "" & t_id & "\\n" & t_name & "\\n" & t_album & "\\n" & t_artist & "\\n" & t_duration',
+ "end tell",
+ "return output"
+ ]);
+
+ const [id, name, album, artist, durationStr] = stdout.split("\n").filter(k => !!k);
+ const duration = Number.parseFloat(durationStr);
+
+ const remoteData = await fetchRemoteData({ id, name, artist, album });
+
+ return { name, album, artist, playerPosition, duration, ...remoteData };
+}
diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx
index 423dce9b5..df307e756 100644
--- a/src/plugins/arRPC.web/index.tsx
+++ b/src/plugins/arRPC.web/index.tsx
@@ -19,11 +19,11 @@
import { popNotice, showNotice } from "@api/Notices";
import { Link } from "@components/Link";
import { Devs } from "@utils/constants";
-import definePlugin from "@utils/types";
-import { findByPropsLazy } from "@webpack";
+import definePlugin, { ReporterTestable } from "@utils/types";
+import { findByCodeLazy } from "@webpack";
import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common";
-const RpcUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL");
+const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID");
async function lookupAsset(applicationId: string, key: string): Promise {
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
@@ -32,7 +32,7 @@ async function lookupAsset(applicationId: string, key: string): Promise
const apps: any = {};
async function lookupApp(applicationId: string): Promise {
const socket: any = {};
- await RpcUtils.fetchApplicationsRPC(socket, applicationId);
+ await fetchApplicationsRPC(socket, applicationId);
return socket.application;
}
@@ -41,6 +41,7 @@ export default definePlugin({
name: "WebRichPresence (arRPC)",
description: "Client plugin for arRPC to enable RPC on Discord Web (experimental)",
authors: [Devs.Ducko],
+ reporterTestable: ReporterTestable.None,
settingsAboutComponent: () => (
<>
diff --git a/src/plugins/banger/index.ts b/src/plugins/banger/index.ts
index dd9b4c82f..7e0d2df73 100644
--- a/src/plugins/banger/index.ts
+++ b/src/plugins/banger/index.ts
@@ -27,7 +27,7 @@ export default definePlugin({
{
find: "BAN_CONFIRM_TITLE.",
replacement: {
- match: /src:\i\("\d+"\)/g,
+ match: /src:\i\("?\d+"?\)/g,
replace: "src: Vencord.Settings.plugins.BANger.source"
}
}
diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx
index 795f19901..d0e8cf34c 100644
--- a/src/plugins/betterFolders/index.tsx
+++ b/src/plugins/betterFolders/index.tsx
@@ -19,7 +19,7 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
-import { findByPropsLazy, findStoreLazy } from "@webpack";
+import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
import { FluxDispatcher, i18n, useMemo } from "@webpack/common";
import FolderSideBar from "./FolderSideBar";
@@ -30,7 +30,7 @@ enum FolderIconDisplay {
MoreThanOneFolderExpanded
}
-const { GuildsTree } = findByPropsLazy("GuildsTree");
+const GuildsTree = findLazy(m => m.prototype?.moveNextTo);
const SortedGuildStore = findStoreLazy("SortedGuildStore");
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
@@ -112,12 +112,12 @@ export default definePlugin({
replacement: [
// Create the isBetterFolders variable in the GuildsBar component
{
- match: /(?<=let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?)(?=}=\i,)/,
- replace: ",isBetterFolders"
+ match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i,)/,
+ replace: "$&,isBetterFolders"
},
// If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders
{
- match: /\[(\i)\]=(\(0,\i\.useStateFromStoresArray\).{0,40}getGuildsTree\(\).+?}\))(?=,)/,
+ match: /\[(\i)\]=(\(0,\i\.\i\).{0,40}getGuildsTree\(\).+?}\))(?=,)/,
replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0].isBetterFolders,betterFoldersOriginalTree,arguments[0].betterFoldersExpandedIds)`
},
// If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children
@@ -139,13 +139,13 @@ export default definePlugin({
},
{
// This is the parent folder component
- find: ".MAX_GUILD_FOLDER_NAME_LENGTH,",
+ find: ".toggleGuildFolderExpand(",
predicate: () => settings.store.sidebar && settings.store.showFolderIcon !== FolderIconDisplay.Always,
replacement: [
{
// Modify the expanded state to instead return the list of expanded folders
- match: /(useStateFromStores\).{0,20}=>)(\i\.\i)\.isFolderExpanded\(\i\)/,
- replace: (_, rest, ExpandedGuildFolderStore) => `${rest}${ExpandedGuildFolderStore}.getExpandedFolders()`,
+ match: /(\],\(\)=>)(\i\.\i)\.isFolderExpanded\(\i\)\)/,
+ replace: (_, rest, ExpandedGuildFolderStore) => `${rest}${ExpandedGuildFolderStore}.getExpandedFolders())`,
},
{
// Modify the expanded prop to use the boolean if the above patch fails, or check if the folder is expanded from the list if it succeeds
@@ -196,7 +196,7 @@ export default definePlugin({
]
},
{
- find: "APPLICATION_LIBRARY,render",
+ find: "APPLICATION_LIBRARY,render:",
predicate: () => settings.store.sidebar,
replacement: {
// Render the Better Folders sidebar
diff --git a/src/plugins/betterGifAltText/index.ts b/src/plugins/betterGifAltText/index.ts
index f0090343e..55fa22525 100644
--- a/src/plugins/betterGifAltText/index.ts
+++ b/src/plugins/betterGifAltText/index.ts
@@ -36,7 +36,7 @@ export default definePlugin({
{
find: ".Messages.GIF,",
replacement: {
- match: /alt:(\i)=(\i\.default\.Messages\.GIF)(?=,[^}]*\}=(\i))/,
+ match: /alt:(\i)=(\i\.\i\.Messages\.GIF)(?=,[^}]*\}=(\i))/,
replace:
// rename prop so we can always use default value
"alt_$$:$1=$self.altify($3)||$2",
diff --git a/src/plugins/betterGifPicker/index.ts b/src/plugins/betterGifPicker/index.ts
index f1608f28c..9d7d8db41 100644
--- a/src/plugins/betterGifPicker/index.ts
+++ b/src/plugins/betterGifPicker/index.ts
@@ -13,7 +13,7 @@ export default definePlugin({
authors: [Devs.Samwich],
patches: [
{
- find: ".GIFPickerResultTypes.SEARCH",
+ find: '"state",{resultType:',
replacement: [{
match: /(?<="state",{resultType:)null/,
replace: '"Favorites"'
diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx
index ecb1ed400..bf4cf0f37 100644
--- a/src/plugins/betterRoleContext/index.tsx
+++ b/src/plugins/betterRoleContext/index.tsx
@@ -5,15 +5,18 @@
*/
import { definePluginSettings } from "@api/Settings";
+import { getUserSettingLazy } from "@api/UserSettings";
import { ImageIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
import { getCurrentGuild, openImageModal } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
-import { Clipboard, GuildStore, Menu, PermissionStore, TextAndImagesSettingsStores } from "@webpack/common";
+import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common";
const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild");
+const DeveloperMode = getUserSettingLazy("appearance", "developerMode")!;
+
function PencilIcon() {
return (