Merge branch 'dev'

This commit is contained in:
thororen1234 2026-02-05 19:26:49 -05:00
commit e418ed4f41
No known key found for this signature in database
26 changed files with 1842 additions and 59 deletions

View file

@ -34,6 +34,7 @@ import { debounce } from "@shared/debounce";
import { IS_WINDOWS } from "@utils/constants";
import { createAndAppendStyle } from "@utils/css";
import { StartAt } from "@utils/types";
import { SettingsRouter } from "@webpack/common";
import { get as dsGet } from "./api/DataStore";
import { popNotice, showNotice } from "./api/Notices";
@ -45,7 +46,6 @@ import { localStorage } from "./utils/localStorage";
import { relaunch } from "./utils/native";
import { checkForUpdates, isOutdated as getIsOutdated, update, UpdateLogger } from "./utils/updater";
import { onceReady } from "./webpack";
import { openUserSettingsPanel } from "./webpack/common";
import { patches } from "./webpack/patchWebpack";
if (IS_REPORTER) {
@ -66,7 +66,7 @@ async function syncSettings() {
title: "Cloud Settings",
body: "Cloud sync was disabled because this account isn't connected to the cloud App. You can enable it again by connecting this account in Cloud Settings. (note: it will store your preferences separately)",
color: "var(--yellow-360)",
onClick: () => openUserSettingsPanel("equicord_cloud")
onClick: () => SettingsRouter.openUserSettings("equicord_cloud_panel")
});
// Disable cloud sync globally
Settings.cloud.authenticated = false;
@ -85,7 +85,7 @@ async function syncSettings() {
body: "We've noticed you have cloud integrations enabled in another client! Due to limitations, you will " +
"need to re-authenticate to continue using them. Click here to go to the settings page to do so!",
color: "var(--yellow-360)",
onClick: () => openUserSettingsPanel("equicord_cloud")
onClick: () => SettingsRouter.openUserSettings("equicord_cloud_panel")
});
return;
}

View file

@ -22,7 +22,7 @@ interface IMessageCreate {
message: Message;
}
const SILENT_PING_FLAG = 1 << 12;
const SILENT_PING_FLAG = 1 << 12;
function Icon(enabled?: boolean): JSX.Element {
return <svg
@ -168,7 +168,7 @@ export default definePlugin({
if (message.state === "SENDING" || message.content === "" || message.author.id === currentUser.id || (channelId === currentChannelId && WindowStore.isFocused()) || userStatus !== settings.store.statusToUse) {
return;
}
if (settings.store.respectSilentPings && (message.flags & SILENT_PING_FLAG)) {return;}
if (settings.store.respectSilentPings && (message.flags & SILENT_PING_FLAG)) { return; }
const mentioned = MessageStore.getMessage(channelId, message.id)?.mentioned;
if ((settings.store.guilds.split(", ").includes(guildId) || settings.store.channels.split(", ").includes(channelId)) && mentioned) {
await showNotification(message, guildId);

View file

@ -26,7 +26,7 @@ export let colors: Record<string, string> = {};
})();
// needed for color picker to be available without opening settings (ty pindms!!)
const requireSettingsMenu = extractAndLoadChunksLazy(['"AppSkeleton"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}\}\),(?=.{0,15}createPromise.*?\}\),)/);
const requireSettingsMenu = extractAndLoadChunksLazy(['type:"USER_SETTINGS_MODAL_OPEN"']);
const ColorIcon = () => {
return (
<svg

View file

@ -0,0 +1,237 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import ErrorBoundary from "@components/ErrorBoundary";
import { classNameFactory } from "@utils/css";
import { classes } from "@utils/misc";
import { findComponentByCodeLazy } from "@webpack";
import { createRoot, React, useEffect, useState, useStateFromStores, VoiceStateStore } from "@webpack/common";
export type GhostState = {
visible: boolean;
x: number;
y: number;
kind: "user" | "channel" | "guild";
title: string;
subtitle?: string;
iconUrl?: string;
symbol?: string;
badge?: string;
entityId?: string;
exiting: boolean;
};
const ScreenArrowIcon = findComponentByCodeLazy("3V5Zm16") as React.ComponentType<{
className?: string;
width?: number;
height?: number;
size?: number;
color?: string;
colorClass?: string;
}>;
const cl = classNameFactory("vc-dragify-");
let ghostRoot: ReturnType<typeof createRoot> | null = null;
let ghostMountNode: HTMLDivElement | null = null;
let ghostRaf: number | null = null;
let ghostPendingPos: { x: number; y: number; } | null = null;
let ghostHideTimer: number | null = null;
let ghostState: GhostState = {
visible: false,
x: 0,
y: 0,
kind: "channel",
title: "",
exiting: false
};
const ghostListeners = new Set<() => void>();
function notifyGhost() {
ghostListeners.forEach(listener => listener());
}
function setGhostState(next: Partial<GhostState>) {
ghostState = { ...ghostState, ...next };
notifyGhost();
}
export function isGhostVisible(): boolean {
return ghostState.visible;
}
export function scheduleGhostPosition(x: number, y: number) {
ghostPendingPos = { x, y };
if (ghostRaf !== null) return;
setGhostState({ x, y });
ghostRaf = requestAnimationFrame(() => {
if (ghostPendingPos) setGhostState({ x: ghostPendingPos.x, y: ghostPendingPos.y });
ghostPendingPos = null;
ghostRaf = null;
});
}
export function hideGhost() {
if (!ghostState.visible) return;
if (ghostHideTimer !== null) {
clearTimeout(ghostHideTimer);
ghostHideTimer = null;
}
setGhostState({ exiting: true });
ghostHideTimer = window.setTimeout(() => {
ghostHideTimer = null;
setGhostState({ visible: false, exiting: false });
}, 200);
}
export function showGhost(next: Omit<GhostState, "visible" | "x" | "y">, position?: { x: number; y: number; }) {
if (ghostHideTimer !== null) {
clearTimeout(ghostHideTimer);
ghostHideTimer = null;
}
if (position) scheduleGhostPosition(position.x, position.y);
setGhostState({ ...next, visible: true, exiting: false });
}
export function mountGhost() {
if (typeof document === "undefined") return;
const { body } = document;
if (!body) return;
if (ghostRoot) {
if (ghostMountNode && !ghostMountNode.isConnected) body.appendChild(ghostMountNode);
return;
}
if (!ghostMountNode) ghostMountNode = document.createElement("div");
if (!ghostMountNode.isConnected) body.appendChild(ghostMountNode);
ghostRoot = createRoot(ghostMountNode);
ghostRoot.render(
<div className={cl("ghost-container")}>
<ErrorBoundary noop>
<DragGhost />
</ErrorBoundary>
</div>
);
}
export function unmountGhost() {
ghostRoot?.unmount();
ghostRoot = null;
ghostMountNode?.remove();
ghostMountNode = null;
if (ghostRaf !== null) {
cancelAnimationFrame(ghostRaf);
ghostRaf = null;
}
ghostPendingPos = null;
if (ghostHideTimer !== null) {
clearTimeout(ghostHideTimer);
ghostHideTimer = null;
}
setGhostState({ visible: false, exiting: false });
}
const DragGhost = () => {
const [state, setState] = useState(ghostState);
useEffect(() => {
const listener = () => setState({ ...ghostState });
ghostListeners.add(listener);
return () => {
ghostListeners.delete(listener);
};
}, []);
const voiceState = useStateFromStores(
[VoiceStateStore],
() => (state.kind === "user" && state.entityId
? VoiceStateStore.getVoiceStateForUser(state.entityId)
: null)
);
const inVoice = voiceState?.channelId;
const isMuted = voiceState && (voiceState.selfMute || voiceState.mute || voiceState.selfDeaf || voiceState.deaf);
const isStreaming = voiceState?.selfStream;
if (!state.visible) return null;
return (
<div
className={classes(cl("ghost"), state.exiting ? cl("ghost-exit") : "")}
style={{ transform: `translate3d(${state.x}px, ${state.y}px, 0)` }}
>
<div className={cl("card")}>
<div className={cl("icon")}>
{state.iconUrl
? <img className={cl("icon-image")} src={state.iconUrl} alt="" />
: <span className={cl("icon-text")}>{state.symbol ?? "#"}</span>
}
</div>
<div className={cl("body")}>
<div className={cl("title-row")}>
<div className={cl("title")}>{state.title}</div>
{state.kind === "user" && inVoice
? (isMuted
? <VoiceMutedIcon className={classes(cl("voice-icon"), cl("voice-icon-muted"))} />
: <VoiceStateIcon className={cl("voice-icon")} />)
: null}
{state.kind === "user" && inVoice && isStreaming
? <ScreenArrowIcon
className={classes(cl("voice-icon"), cl("voice-icon-stream"))}
size={14}
width={14}
height={14}
/>
: null}
</div>
{state.subtitle && <div className={cl("subtitle")}>{state.subtitle}</div>}
</div>
<div className={cl("badge")}>{state.badge ?? state.kind}</div>
</div>
</div>
);
};
function VoiceStateIcon({ className, size = 14 }: { className?: string; size?: number; }) {
return (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
fill="none"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M7 2a1 1 0 0 0-1 1v18a1 1 0 1 0 2 0V3a1 1 0 0 0-1-1ZM11 6a1 1 0 1 1 2 0v12a1 1 0 1 1-2 0V6ZM1 8a1 1 0 0 1 2 0v8a1 1 0 1 1-2 0V8ZM16 5a1 1 0 1 1 2 0v14a1 1 0 1 1-2 0V5ZM22 8a1 1 0 0 0-1 1v6a1 1 0 1 0 2 0V9a1 1 0 0 0-1-1Z"
/>
</svg>
);
}
function VoiceMutedIcon({ className, size = 14 }: { className?: string; size?: number; }) {
return (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
fill="none"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M22.7 2.7a1 1 0 0 0-1.4-1.4l-20 20a1 1 0 1 0 1.4 1.4l20-20ZM6.85 13.15a.5.5 0 0 1-.85-.36V3a1 1 0 0 1 2 0v8.8a.5.5 0 0 1-.15.35l-1 1ZM11 17.2v.8a1 1 0 1 0 2 0v-1.8a.5.5 0 0 0-.85-.35l-1 1a.5.5 0 0 0-.15.36ZM11 7.8V6a1 1 0 1 1 2 0v.8a.5.5 0 0 1-.15.35l-1 1a.5.5 0 0 1-.85-.36ZM17.15 10.85a.5.5 0 0 1 .85.36V19a1 1 0 1 1-2 0v-6.8a.5.5 0 0 1 .15-.35l1-1ZM2 7a1 1 0 0 0-1 1v8a1 1 0 1 0 2 0V8a1 1 0 0 0-1-1ZM21 9a1 1 0 1 1 2 0v6a1 1 0 1 1-2 0V9Z"
/>
</svg>
);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,149 @@
.vc-dragify-ghost-container {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 9999;
}
.vc-dragify-ghost {
position: fixed;
top: 0;
left: 0;
pointer-events: none;
color: var(--text-normal, #fff);
opacity: 1;
animation: vc-dragify-fade-in 200ms ease-out forwards;
will-change: transform;
}
.vc-dragify-ghost-exit {
animation: vc-dragify-fade-out 200ms ease-in forwards;
}
.vc-dragify-card {
display: flex;
align-items: center;
gap: 12px;
padding: 6px 10px;
min-width: 220px;
max-width: 320px;
border-radius: 6px;
background: rgb(0 0 0 / 70%);
border: 1px solid rgb(255 255 255 / 10%);
box-shadow: 0 12px 40px rgb(0 0 0 / 35%);
backdrop-filter: blur(12px);
transform: translateZ(0);
color: var(--text-default, #fff);
font-family: var(--font-code);
line-height: 1.4;
animation: none;
will-change: transform;
}
.vc-dragify-icon {
width: 28px;
height: 28px;
border-radius: 6px;
background: rgb(255 255 255 / 10%);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
flex-shrink: 0;
color: var(--text-default, #fff);
font-weight: 600;
}
.vc-dragify-icon-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.vc-dragify-icon-text {
font-size: 13px;
color: var(--text-default, #fff);
}
.vc-dragify-body {
display: flex;
flex-direction: column;
min-width: 0;
flex: 1;
}
.vc-dragify-title-row {
display: flex;
align-items: center;
gap: 6px;
min-width: 0;
}
.vc-dragify-title {
font-size: 14px;
font-weight: 600;
color: var(--text-default, #fff);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.vc-dragify-voice-icon {
flex-shrink: 0;
color: var(--interactive-normal, #b9bbbe);
}
.vc-dragify-voice-icon-muted {
color: var(--status-danger, #ed4245);
}
.vc-dragify-voice-icon-stream {
color: var(--status-positive, #3ba55d);
}
.vc-dragify-subtitle {
font-size: 11px;
color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.vc-dragify-badge {
font-size: 10px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-muted);
margin-left: auto;
}
@keyframes vc-dragify-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes vc-dragify-fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.vc-dragify-ghost {
animation: none;
}
.vc-dragify-ghost-exit {
animation: none;
opacity: 0;
}
}

View file

@ -0,0 +1,182 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import type { Channel, Guild, User } from "@vencord/discord-types";
const userMentionRegex = /<@!?(\d{17,20})>/;
const userProfileUrlRegex = /discord(?:(?:app)?\.com|:\/\/-?)\/users\/(\d{17,20})/;
const userAvatarRegex = /cdn\.discordapp\.com\/(?:avatars|users)\/(\d{17,20})\//;
const guildUserAvatarRegex = /cdn\.discordapp\.com\/guilds\/\d{17,20}\/users\/(\d{17,20})\/avatars\//;
const channelMentionRegex = /<#(\d{17,20})>/;
const channelUrlRegex = /discord(?:(?:app)?\.com|:\/\/-?)\/channels\/(?:(@me)|(\d{17,20}))\/(\d{17,20})/;
const channelPathRegex = /\/channels\/(@me|\d{17,20})\/(\d{17,20})/;
const guildIconRegex = /cdn\.discordapp\.com\/icons\/(\d{17,20})\//;
export type DropEntity =
| { kind: "user"; id: string; }
| { kind: "channel"; id: string; guildId?: string; }
| { kind: "guild"; id: string; };
interface DragifyPayload {
id?: string;
userId?: string;
channelId?: string;
guildId?: string;
type?: string;
kind?: string;
itemType?: string;
}
type StoreSet = {
ChannelStore: { getChannel(id: string): Channel | null | undefined; };
GuildStore: { getGuild(id: string): Guild | null | undefined; };
UserStore: { getUser(id: string): User | null | undefined; };
};
export function tryParseJson<T = Record<string, unknown>>(value: string): T | null {
if (!value || value.length < 2 || (value[0] !== "{" && value[0] !== "[")) return null;
try {
const parsed = JSON.parse(value);
return typeof parsed === "object" && parsed ? parsed as T : null;
} catch {
return null;
}
}
export function extractSnowflakeFromString(value: string): string | null {
const match = value.match(/\d{17,20}/);
return match?.[0] ?? null;
}
export function extractSnowflakes(values: string[]): string[] {
const ids = new Set<string>();
const regex = /\d{17,20}/g;
for (const value of values) {
const matches = value.match(regex);
if (matches) matches.forEach(id => ids.add(id));
}
return Array.from(ids);
}
export function extractStrings(dataTransfer: DataTransfer): string[] {
const collected = new Set<string>();
const add = (v?: string) => v && collected.add(v);
add(dataTransfer.getData("text/plain"));
add(dataTransfer.getData("text/uri-list"));
add(dataTransfer.getData("text/html"));
add(dataTransfer.getData("text/x-moz-url"));
add(dataTransfer.getData("application/json"));
for (const type of dataTransfer.types ?? []) add(dataTransfer.getData(type));
const split: string[] = [];
for (const v of collected) {
split.push(v);
if (v.includes("\n")) split.push(...v.split(/\s+/).filter(Boolean));
}
return Array.from(new Set(split));
}
export async function collectPayloadStrings(dataTransfer: DataTransfer): Promise<string[]> {
const sync = extractStrings(dataTransfer);
const asyncValues: string[] = [];
const itemPromises = Array.from(dataTransfer.items ?? [])
.filter(item => item.kind === "string")
.map(item => new Promise<void>(resolve => item.getAsString(val => {
if (val) asyncValues.push(val);
resolve();
})));
await Promise.all(itemPromises);
return Array.from(new Set([...sync, ...asyncValues]));
}
export function parseFromStrings(payloads: string[], stores: StoreSet): DropEntity | null {
if (payloads.length === 0) return null;
for (const value of payloads) {
const parsed = tryParseJson<DragifyPayload>(value);
if (parsed) {
const id = parsed.id ?? parsed.userId ?? parsed.channelId ?? parsed.guildId;
const type = (parsed.type ?? parsed.kind ?? parsed.itemType ?? "").toLowerCase();
if (id) {
if (type.includes("user")) return { kind: "user", id };
if (type.includes("channel")) return { kind: "channel", id, guildId: parsed.guildId };
if (type.includes("guild") || type.includes("server")) return { kind: "guild", id };
}
}
}
for (const value of payloads) {
const trimmed = value.trim();
if (!trimmed) continue;
const userFromMention = userMentionRegex.exec(trimmed);
if (userFromMention) return { kind: "user", id: userFromMention[1] };
const userFromProfile = userProfileUrlRegex.exec(trimmed);
if (userFromProfile) return { kind: "user", id: userFromProfile[1] };
const guildUserAvatar = guildUserAvatarRegex.exec(trimmed);
if (guildUserAvatar) return { kind: "user", id: guildUserAvatar[1] };
const userFromAvatar = userAvatarRegex.exec(trimmed);
if (userFromAvatar) return { kind: "user", id: userFromAvatar[1] };
}
for (const value of payloads) {
const trimmed = value.trim();
if (!trimmed) continue;
const channelFromMention = channelMentionRegex.exec(trimmed);
if (channelFromMention) return { kind: "channel", id: channelFromMention[1] };
const channelFromUrl = channelUrlRegex.exec(trimmed);
if (channelFromUrl) {
const guildId = channelFromUrl[1] === "@me" ? "@me" : channelFromUrl[2];
return { kind: "channel", id: channelFromUrl[3], guildId: guildId ?? undefined };
}
}
for (const value of payloads) {
const trimmed = value.trim();
if (!trimmed) continue;
const guildFromIcon = guildIconRegex.exec(trimmed);
if (guildFromIcon) return { kind: "guild", id: guildFromIcon[1] };
}
const candidates = extractSnowflakes(payloads);
for (const candidate of candidates) {
if (stores.ChannelStore.getChannel(candidate)) return { kind: "channel", id: candidate };
if (stores.GuildStore.getGuild(candidate)) return { kind: "guild", id: candidate };
if (stores.UserStore.getUser(candidate)) return { kind: "user", id: candidate };
}
return null;
}
export function extractChannelPath(value: string): { guildId?: string; channelId?: string; } | null {
const channelPath = channelPathRegex.exec(value);
if (!channelPath) return null;
return {
guildId: channelPath[1] === "@me" ? "@me" : channelPath[1],
channelId: channelPath[2]
};
}
export function extractChannelFromUrl(value: string): { guildId?: string; channelId?: string; } | null {
const channelFromUrl = channelUrlRegex.exec(value);
if (!channelFromUrl) return null;
const guildId = channelFromUrl[1] ?? channelFromUrl[2];
return {
guildId: guildId === "@me" ? "@me" : guildId,
channelId: channelFromUrl[3]
};
}
export function extractUserFromAvatar(value: string): string | null {
const guildUserAvatar = guildUserAvatarRegex.exec(value);
if (guildUserAvatar?.[1]) return guildUserAvatar[1];
const userAvatar = userAvatarRegex.exec(value);
if (userAvatar?.[1]) return userAvatar[1];
return null;
}
export function extractUserFromProfile(value: string): string | null {
const match = userProfileUrlRegex.exec(value);
return match?.[1] ?? null;
}

View file

@ -30,6 +30,7 @@ function GroupDmsIcon({ channel }: { channel: Channel; }) {
aria-label={channel?.name || "Unnamed Group"}
/> : <GroupDmsRecipientsIcon
recipients={channel?.recipients ?? []}
channel={channel}
size="SIZE_40"
isTyping={null}
/>;

View file

@ -1,4 +1,4 @@
/*
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
@ -94,7 +94,7 @@ const handleMessage = ((channelId, message) => {
const wordMatch: WordMatch | null = chooseRandomWord(msg);
if (wordMatch === null) return;
const word = wordMatch.word;
const { word } = wordMatch;
const wordUpper = word.toUpperCase();
const isUpper = word === wordUpper;

View file

@ -15,7 +15,7 @@ export default definePlugin({
{
find: "#{intl::COPY_MESSAGE_LINK}",
replacement: {
match: /(?<=(?:.concat\(|\/\/\$\{))location\.host/,
match: /(?<=\/\/\$\{)location\.host/,
replace: "$self.normalizeHost(location.host)",
},
},

View file

@ -17,7 +17,7 @@ import RemixModal from "./RemixModal";
import css from "./styles.css?managed";
const requireCreateStickerModal = extractAndLoadChunksLazy([".CREATE_STICKER_MODAL,", "isDisplayingIndividualStickers"]);
const requireSettingsMenu = extractAndLoadChunksLazy(['"AppSkeleton"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}\}\),(?=.{0,15}createPromise.*?\}\),)/);
const requireSettingsMenu = extractAndLoadChunksLazy(['type:"USER_SETTINGS_MODAL_OPEN"']);
const PendingReplyStore = findStoreLazy("PendingReplyStore");
const validMediaTypes = ["image/png", "image/jpeg", "image/jpg", "image/webp"];

View file

@ -29,7 +29,7 @@ const cl = classNameFactory("vc-userpfp-");
const DONO_URL = "https://ko-fi.com/coolesding";
const INVITE_LINK = "userpfp-1129784704267210844";
export const requireSettingsMenu = extractAndLoadChunksLazy(['"AppSkeleton"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}\}\),(?=.{0,15}createPromise.*?\}\),)/);
export const requireSettingsModal = extractAndLoadChunksLazy(['type:"USER_SETTINGS_MODAL_OPEN"']);
export const KEY_DATASTORE = "vencord-custom-avatars";
export const data = { avatars: {} as Record<string, string> };

View file

@ -14,7 +14,7 @@ export default definePlugin({
patches: [
{
// TODO: bundler compat
find: /\.renderLinkComponent[)?]\?/,
find: ".renderLinkComponent",
replacement: {
// widthAndHeightPassed = w != null && w !== 0 && h == null || h === 0
match: /(?<=\i=)(null!=\i&&0!==\i)&&(null!=\i&&0!==\i)/,

View file

@ -100,14 +100,9 @@ export default definePlugin({
{
find: "this.renderArtisanalHack()",
replacement: [
{ // Fade in on layer
// class Layer {
// static contextType = ...;
// ...
// }
// class OtherClass {
match: /(static contextType=\i\.\i;.+?})(?=class)(?<=class (\i).+?)/,
replace: (_, before, Layer) => `${before};${Layer}=$self.Layer;`,
{
match: /class (\i)(?= extends \i\.PureComponent.+?static contextType=.+?jsx\)\(\1,\{mode:)/,
replace: "var $1=$self.Layer;class VencordPatchedOldFadeLayer",
predicate: () => settings.store.disableFade
},
{ // Lazy-load contents

View file

@ -164,12 +164,10 @@ export default definePlugin({
},
{
find: "RPCServer:WSS",
replacement: [
{
match: /\i\.error\(`Error: \$\{(\i)\.message\}/,
replace: '!$1.message.includes("EADDRINUSE")&&$&'
}
]
replacement: {
match: /\i\.error\(`Error: \$\{(\i)\.message\}/,
replace: '!$1.message.includes("EADDRINUSE")&&$&'
}
},
{
find: "Tried getting Dispatch instance before instantiated",

View file

@ -103,6 +103,10 @@ export default definePlugin({
].map(find => ({
find,
replacement: [
{
match: /(?<=userValue:)((\i)(?:.{0,10})?\.avatarDecoration)/,
replace: "$self.useUserDecorAvatarDecoration($2)??$1"
},
{
match: /(?<=userValue:)((\i)(?:.{0,10})?\.avatarDecoration)/,
replace: "$self.useUserDecorAvatarDecoration($2)??$1"

View file

@ -25,18 +25,20 @@ export default definePlugin({
authors: [Devs.Nuckyz],
patches: [
{
find: "#{intl::BOT_CALL_IDLE_DISCONNECT_2}",
find: "this.idleTimeout.start(",
replacement: {
match: /,?(?=\i\(this,"idleTimeout",new \i\.\i\))/,
replace: ";return;"
match: /this\.idleTimeout\.(start|stop)/g,
replace: "$self.noop"
}
},
{
find: "handleIdleUpdate(){",
replacement: {
match: /(?<=_initialize\(\){)/,
replace: "return;"
match: "handleIdleUpdate(){",
replace: "handleIdleUpdate(){return;"
}
}
]
],
noop() { }
});

View file

@ -52,12 +52,10 @@ export default definePlugin({
patches: [{
// HEY THOR UPDATE SHOW ME YOUR NAME IF THIS SHIT CHANGES TY :)
find: ".USER_MENTION)",
replacement: [
{
match: /children:`@\$\{(\i\?\?\i)\}`(?<=\.useName\((\i)\).+?)/,
replace: "children:$self.renderUsername({username:$1,user:$2,showMeYourNameMention:typeof showMeYourNameMention!=='undefined'?showMeYourNameMention:undefined})"
}
]
replacement: {
match: /children:`@\$\{(\i\?\?\i)\}`(?<=\.useName\((\i)\).+?)/,
replace: "children:$self.renderUsername({username:$1,user:$2,showMeYourNameMention:typeof showMeYourNameMention!=='undefined'?showMeYourNameMention:undefined})"
}
},
{
find: ".ROLE_MENTION)",

View file

@ -26,8 +26,7 @@ interface ColorPickerWithSwatchesProps {
const ColorPickerWithSwatches = findComponentByCodeLazy<ColorPickerWithSwatchesProps>('id:"color-picker"');
// 701663 this is terrible imo
export const requireSettingsMenu = extractAndLoadChunksLazy(['"AppSkeleton"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}\}\),(?=.{0,15}createPromise.*?\}\),)/);
export const requireSettingsModal = extractAndLoadChunksLazy(['type:"USER_SETTINGS_MODAL_OPEN"']);
const cl = classNameFactory("vc-pindms-modal-");
@ -122,6 +121,6 @@ export function NewCategoryModal({ categoryId, modalProps, initialChannelId }: P
export const openCategoryModal = (categoryId: string | null, channelId: string | null) =>
openModalLazy(async () => {
await requireSettingsMenu();
await requireSettingsModal();
return modalProps => <NewCategoryModal categoryId={categoryId} modalProps={modalProps} initialChannelId={channelId} />;
});

View file

@ -16,11 +16,6 @@ export interface Category {
collapsed?: boolean;
}
const CATEGORY_BASE_KEY = "PinDMsCategories-";
const CATEGORY_MIGRATED_PINDMS_KEY = "PinDMsMigratedPinDMs";
const CATEGORY_MIGRATED_KEY = "PinDMsMigratedOldCategories";
const OLD_CATEGORY_KEY = "BetterPinDMsCategories-";
let forceUpdateDms: (() => void) | undefined = undefined;
export let currentUserCategories: Category[] = [];

View file

@ -16,7 +16,7 @@ import { findCssClassesLazy, findStoreLazy } from "@webpack";
import { Clickable, ContextMenuApi, FluxDispatcher, Menu, React } from "@webpack/common";
import { contextMenus } from "./components/contextMenu";
import { openCategoryModal, requireSettingsMenu } from "./components/CreateCategoryModal";
import { openCategoryModal, requireSettingsModal } from "./components/CreateCategoryModal";
import { DEFAULT_CHUNK_SIZE } from "./constants";
import { canMoveCategory, canMoveCategoryInDirection, Category, categoryLen, collapseCategory, getAllUncollapsedChannels, getCategoryByIndex, getSections, init, isPinned, moveCategory, removeCategory, usePinnedDms } from "./data";
@ -173,7 +173,7 @@ export default definePlugin({
categoryLen,
getSections,
getAllUncollapsedChannels,
requireSettingsMenu,
requireSettingsMenu: requireSettingsModal,
makeProps(instance, { sections }: { sections: number[]; }) {
this._instance = instance;

View file

@ -40,7 +40,7 @@ export interface Session {
const SessionsStore = findStoreLazy("SessionsStore") as {
getSessions(): Record<string, Session>;
};
const { useStatusFillColor } = mapMangledModuleLazy("translate(${.5625*", {
const { useStatusFillColor } = mapMangledModuleLazy([".5625*", "translate"], {
useStatusFillColor: filters.byCode(".hex")
});

View file

@ -52,7 +52,7 @@ export default definePlugin({
},
},
{
find: /\.INVITES_DISABLED\)(?:\))?\|\|/,
find: "INVITES_DISABLED)||",
predicate: () => settings.store.showInvitesPaused,
replacement: {
match: /\i\.\i\.can\(\i\.\i.MANAGE_GUILD,\i\)/,

View file

@ -131,7 +131,7 @@ export default definePlugin({
replace: "$self.renderTypingUsers({ users: arguments[0]?.typingUserObjects, guildId: arguments[0]?.channel?.guild_id, children: $& })"
},
{
match: /(?<=function \i\(\i\)\{)(?=[^}]+?\{channel:\i,isThreadCreation:\i=!1,...\i\})/,
match: /(?<=function \i\(\i\)\{)(?=[^}]+?\{channel:\i,isThreadCreation:\i=!1,\.\.\.\i\})/,
replace: "let typingUserObjects = $self.useTypingUsers(arguments[0]?.channel);"
},
{

View file

@ -205,12 +205,10 @@ export default definePlugin({
// Banners
{
find: 'backgroundColor:"COMPLETE"',
replacement: [
{
match: /(overflow:"visible",.{0,125}?!1\),)style:{(?=.+?backgroundImage:null!=(\i)\?`url\(\$\{\2\}\))/,
replace: (_, rest, bannerSrc) => `${rest}onClick:()=>${bannerSrc}!=null&&$self.openBanner(${bannerSrc}),style:{cursor:${bannerSrc}!=null?"pointer":void 0,`
}
]
replacement: {
match: /(overflow:"visible",.{0,125}?!1\),)style:{(?=.+?backgroundImage:null!=(\i)\?`url\(\$\{\2\}\))/,
replace: (_, rest, bannerSrc) => `${rest}onClick:()=>${bannerSrc}!=null&&$self.openBanner(${bannerSrc}),style:{cursor:${bannerSrc}!=null?"pointer":void 0,`
}
},
// Group DMs top small & large icon
{

View file

@ -19,7 +19,7 @@
import { Devs, IS_MAC } from "@utils/constants";
import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { ComponentDispatch, FluxDispatcher, NavigationRouter, openUserSettingsPanel, SelectedGuildStore } from "@webpack/common";
import { ComponentDispatch, FluxDispatcher, NavigationRouter, SelectedGuildStore, SettingsRouter } from "@webpack/common";
const KeyBinds = findByPropsLazy("JUMP_TO_GUILD", "SERVER_NEXT");
@ -55,7 +55,7 @@ export default definePlugin({
break;
case ",":
e.preventDefault();
openUserSettingsPanel("my_account");
SettingsRouter.openUserSettings("my_account_panel");
break;
default:
if (e.key >= "1" && e.key <= "9") {