fix plugin settings UI

This commit is contained in:
Vendicated 2026-01-23 17:51:30 +01:00
parent 8c81e5d27e
commit 702a71dee8
No known key found for this signature in database
GPG key ID: D66986BAF75ECF18
5 changed files with 52 additions and 8 deletions

View file

@ -31,7 +31,7 @@ import { classes, isObjectEmpty } from "@utils/misc";
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { OptionType, Plugin } from "@utils/types";
import { User } from "@vencord/discord-types";
import { findByPropsLazy } from "@webpack";
import { findCssClassesLazy } from "@webpack";
import { Clickable, FluxDispatcher, Forms, React, Text, Tooltip, useEffect, UserStore, UserSummaryItem, UserUtils, useState } from "@webpack/common";
import { Constructor } from "type-fest";
@ -43,7 +43,7 @@ import { GithubButton, WebsiteButton } from "./LinkIconButton";
const cl = classNameFactory("vc-plugin-modal-");
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
const AvatarStyles = findCssClassesLazy("moreUsers", "avatar", "clickableAvatar");
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
interface PluginModalProps extends ModalProps {

View file

@ -104,9 +104,9 @@ interface Modals {
export const Modals: Modals = mapMangledModuleLazy(':"thin")', {
ModalRoot: filters.componentByCode('.MODAL,"aria-labelledby":'),
ModalHeader: filters.componentByCode(",id:"),
ModalContent: filters.componentByCode(".content,"),
ModalFooter: filters.componentByCode(".footer,"),
ModalCloseButton: filters.componentByCode(".close]:")
ModalContent: filters.componentByCode("scrollbarType:"),
ModalFooter: filters.componentByCode(".HORIZONTAL_REVERSE,"),
ModalCloseButton: filters.componentByCode(".withCircleBackground")
});
export const ModalRoot = LazyComponent(() => Modals.ModalRoot);

View file

@ -146,3 +146,8 @@ export const ZWSP = "\u200b";
export function toInlineCode(s: string) {
return "``" + ZWSP + s.replaceAll("`", ZWSP + "`" + ZWSP) + ZWSP + "``";
}
// @ts-expect-error Missing RegExp.escape
export const escapeRegExp: (s: string) => string = RegExp.escape ?? function (s: string) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};

View file

@ -60,7 +60,7 @@ export const TooltipContainer = TooltipContainerComponent as never;
export const TextInput = waitForComponent<t.TextInput>("TextInput", filters.componentByCode("#{intl::MAXIMUM_LENGTH_ERROR}", '"input"'));
export const TextArea = waitForComponent<t.TextArea>("TextArea", filters.componentByCode("this.getPaddingRight()},id:"));
export const Select = waitForComponent<t.Select>("Select", filters.componentByCode('"Select"', ".newOptionLabel"));
export const Select = waitForComponent<t.Select>("Select", filters.componentByCode('"Select"'));
export const SearchableSelect = waitForComponent<t.SearchableSelect>("SearchableSelect", filters.componentByCode('"SearchableSelect"'));
export const Slider = waitForComponent<t.Slider>("Slider", filters.componentByCode('"markDash".concat('));
export const Popout = waitForComponent<t.Popout>("Popout", filters.componentByCode("ref:this.ref,", "renderPopout:this.renderPopout,"));

View file

@ -20,6 +20,7 @@ import { makeLazy, proxyLazy } from "@utils/lazy";
import { LazyComponent } from "@utils/lazyReact";
import { Logger } from "@utils/Logger";
import { canonicalizeMatch } from "@utils/patches";
import { escapeRegExp } from "@utils/text";
import type { FluxStore } from "@vencord/discord-types";
import type { ModuleExports, ModuleFactory, WebpackRequire } from "@vencord/discord-types/webpack";
@ -53,6 +54,10 @@ export const stringMatches = (s: string, filter: CodeFilter) =>
: (f.global && (f.lastIndex = 0), f.test(s))
);
export function makeClassNameRegex(className: string) {
return new RegExp(`(?:\\b|_)${escapeRegExp(className)}(?:\\b|_)`);
}
export const filters = {
byProps: (...props: PropsFilter): FilterFn =>
props.length === 1
@ -90,6 +95,17 @@ export const filters = {
filter.$$vencordProps = [...code];
return filter;
},
byClassNames: (...classes: string[]): FilterFn => {
const regexes = classes.map(makeClassNameRegex);
return (m: any) => {
if (typeof m !== "object") return false;
const values = Object.values(m);
return regexes.every(cls => values.some(v => typeof v === "string" && cls.test(v)));
};
}
};
@ -209,7 +225,7 @@ export function handleModuleNotFound(method: string, ...filter: unknown[]) {
/**
* Find the first module that matches the filter
*/
export const find = traceFunction("find", function find(filter: FilterFn, { isIndirect = false, isWaitFor = false }: { isIndirect?: boolean; isWaitFor?: boolean; } = {}) {
export const find = traceFunction("find", function find(filter: FilterFn, { isIndirect = false, isWaitFor = false, topLevelOnly = false }: { isIndirect?: boolean; isWaitFor?: boolean; topLevelOnly?: boolean; } = {}) {
if (IS_ANTI_CRASH_TEST) return null;
if (typeof filter !== "function")
@ -223,7 +239,7 @@ export const find = traceFunction("find", function find(filter: FilterFn, { isIn
return isWaitFor ? [mod.exports, key] : mod.exports;
}
if (typeof mod.exports !== "object") continue;
if (typeof mod.exports !== "object" || topLevelOnly) continue;
for (const nestedMod in mod.exports) {
const nested = mod.exports[nestedMod];
@ -562,6 +578,29 @@ export function findExportedComponentLazy<T extends object = any>(...props: Prop
});
}
export function findCssClasses<S extends string>(...classes: S[]): Record<S, string> {
const res = find(filters.byClassNames(...classes), { isIndirect: true, topLevelOnly: true });
if (!res)
handleModuleNotFound("findCssClasses", ...classes);
const values = Object.values(res);
const mapped = {} as Record<S, string>;
for (const cls of classes) {
const re = makeClassNameRegex(cls);
mapped[cls] = values.find(v => typeof v === "string" && re.test(v)) as string;
}
return mapped;
}
export function findCssClassesLazy<S extends string>(...classes: S[]) {
if (IS_REPORTER) lazyWebpackSearchHistory.push(["findCssClasses", classes]);
return proxyLazy(() => findCssClasses(...classes));
}
function getAllPropertyNames(object: Record<PropertyKey, any>, includeNonEnumerable: boolean) {
const names = new Set<PropertyKey>();