Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
thororen1234 2026-02-10 18:08:23 -05:00
commit af2eaab28b
No known key found for this signature in database
5 changed files with 103 additions and 218 deletions

View file

@ -713,3 +713,19 @@ export function CloudUploadIcon(props: IconProps) {
</Icon>
);
}
export const ClockIcon = (props?: any) => {
return (
<Icon
{...props}
viewBox="0 0 24 24"
>
<path
fillRule="evenodd"
clipRule="evenodd"
fill={props.fill || "currentColor"}
d="M12 23a11 11 0 1 0 0-22 11 11 0 0 0 0 22Zm1-18a1 1 0 1 0-2 0v7c0 .27.1.52.3.7l3 3a1 1 0 0 0 1.4-1.4L13 11.58V5Z"
/>
</Icon>
);
};

View file

@ -18,7 +18,6 @@ import {
} from "@components/settings";
import { gitHashShort } from "@shared/vencordUserAgent";
import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import { isTruthy } from "@utils/guards";
import definePlugin, { IconProps, OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
@ -104,7 +103,7 @@ const settings = definePluginSettings({
}
});
export const settingsSectionMap: [string, string][] = [
const settingsSectionMap: [string, string][] = [
["EquicordSettings", "equicord_main_panel"],
["EquicordPlugins", "equicord_plugins_panel"],
["EquicordThemes", "equicord_themes_panel"],
@ -123,6 +122,7 @@ export default definePlugin({
required: true,
settings,
settingsSectionMap,
patches: [
{
@ -301,126 +301,6 @@ export default definePlugin({
customSections: [] as ((SectionTypes: Record<string, string>) => { section: string; element: ComponentType; label: string; id?: string; })[],
customEntries: [] as EntryOptions[],
makeSettingsCategories(SectionTypes: Record<string, string>) {
return [
{
section: SectionTypes.HEADER,
label: "Equicord",
className: "vc-settings-header",
},
{
section: "EquicordSettings",
label: "Equicord",
element: VencordTab,
className: "vc-settings",
},
{
section: "EquicordPlugins",
label: "Plugins",
searchableTitles: ["Plugins"],
element: PluginsTab,
className: "vc-plugins",
},
{
section: "EquicordThemes",
label: "Themes",
searchableTitles: ["Themes"],
element: ThemesTab,
className: "vc-themes",
},
!IS_UPDATER_DISABLED && {
section: "EquicordUpdater",
label: "Updater",
searchableTitles: ["Updater"],
element: UpdaterTab,
className: "vc-updater",
},
{
section: "EquicordChangelog",
label: "Changelog",
searchableTitles: ["Changelog"],
element: ChangelogTab,
className: "vc-changelog",
},
{
section: "EquicordCloud",
label: "Cloud",
searchableTitles: ["Cloud"],
element: CloudTab,
className: "vc-cloud",
},
{
section: "EquicordBackupAndRestore",
label: "Backup & Restore",
searchableTitles: ["Backup & Restore"],
element: BackupAndRestoreTab,
className: "vc-backup-restore",
},
IS_DEV && {
section: "EquicordPatchHelper",
label: "Patch Helper",
searchableTitles: ["Patch Helper"],
element: PatchHelperTab,
className: "vc-patch-helper",
},
...this.customSections.map(func => func(SectionTypes)),
{
section: SectionTypes.DIVIDER,
},
].filter(Boolean);
},
isRightSpot({ header, settings: s }: { header?: string; settings?: string[]; }) {
const firstChild = s?.[0];
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true;
const { settingsLocation } = settings.store;
if (settingsLocation === "bottom") return firstChild === "LOGOUT";
if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG";
if (!header) return;
try {
const names: Record<Exclude<SettingsLocation, "bottom" | "belowActivity">, string> = {
top: getIntlMessage("USER_SETTINGS"),
aboveNitro: getIntlMessage("BILLING_SETTINGS"),
belowNitro: getIntlMessage("APP_SETTINGS"),
aboveActivity: getIntlMessage("ACTIVITY_SETTINGS"),
};
if (!names[settingsLocation] || names[settingsLocation].endsWith("_SETTINGS"))
return firstChild === "PREMIUM";
return header === names[settingsLocation];
} catch {
return firstChild === "PREMIUM";
}
},
patchedSettings: new WeakSet(),
addSettings(
elements: any[],
element: { header?: string; settings: string[]; },
SectionTypes: Record<string, string>,
) {
if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return;
this.patchedSettings.add(elements);
elements.push(...this.makeSettingsCategories(SectionTypes));
},
wrapSettingsHook(originalHook: (...args: any[]) => Record<string, unknown>[]) {
return (...args: any[]) => {
const elements = originalHook(...args);
if (!this.patchedSettings.has(elements))
elements.unshift(...this.makeSettingsCategories({ HEADER: SectionType.HEADER, DIVIDER: SectionType.DIVIDER, CUSTOM: SectionType.CUSTOM }) as Record<string, unknown>[]);
return elements;
};
},
get electronVersion() {
return VencordNative.native.getVersions().electron ?? window.legcord?.electron ?? null;
},

View file

@ -102,12 +102,10 @@ export default definePlugin({
"#{intl::ayozFl::raw}", // Avatar preview
].map(find => ({
find,
replacement: [
{
match: /(?<=userValue:)((\i(?:\.author)?)\?\.avatarDecoration)/,
replace: "$self.useUserDecorAvatarDecoration($2)??$1"
}
]
replacement: {
match: /(?<=userValue:)((\i(?:\.author)?)\?\.avatarDecoration)/,
replace: "$self.useUserDecorAvatarDecoration($2)??$1"
}
})),
// Patch avatar decoration preview to display Decor avatar decorations as if they are purchased
{

View file

@ -16,8 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { ClockIcon } from "@equicordplugins/themeLibrary/utils/Icons";
import SettingsPlugin, { settingsSectionMap } from "@plugins/_core/settings";
import { ClockIcon } from "@components/Icons";
import SettingsPlugin from "@plugins/_core/settings";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
@ -28,32 +28,20 @@ export default definePlugin({
description: "Adds Startup Timings to the Settings menu",
authors: [Devs.Megu],
start() {
const { customEntries, customSections } = SettingsPlugin;
customEntries.push({
key: "equicord_startup_timings",
SettingsPlugin.customEntries.push({
key: "vencord_startup_timings",
title: "Startup Timings",
Component: StartupTimingPage,
Icon: ClockIcon
});
customSections.push(() => ({
section: "EquicordStartupTimings",
label: "Startup Timings",
searchableTitles: ["Startup Timings"],
element: StartupTimingPage,
id: "EquicordStartupTimings",
}));
settingsSectionMap.push(["EquicordStartupTimings", "equicord_startup_timings"]);
SettingsPlugin.settingsSectionMap.push(["VencordStartupTimings", "vencord_startup_timings"]);
},
stop() {
const { customEntries, customSections } = SettingsPlugin;
const entryIdx = customEntries.findIndex(e => e.key === "equicord_startup_timings");
if (entryIdx !== -1) customEntries.splice(entryIdx, 1);
const section = customSections.findIndex(section => section({} as any).id === "EquicordStartupTimings");
if (section !== -1) customSections.splice(section, 1);
const map = settingsSectionMap.findIndex(entry => entry[1] === "equicord_startup_timings");
if (map !== -1) customEntries.splice(map, 1);
function removeFromArray<T>(arr: T[], predicate: (e: T) => boolean) {
const idx = arr.findIndex(predicate);
if (idx !== -1) arr.splice(idx, 1);
}
removeFromArray(SettingsPlugin.customEntries, e => e.key === "vencord_startup_timings");
removeFromArray(SettingsPlugin.settingsSectionMap, entry => entry[1] === "vencord_startup_timings");
},
});

View file

@ -611,84 +611,87 @@ function patchFactory(moduleId: PropertyKey, originalFactory: AnyModuleFactory):
markedAsPatched = true;
}
} catch (err) {
logger.error(`Patch by ${patch.plugin} errored (Module id is ${String(moduleId)}): ${replacement.match}\n`, err);
// FIXME: Maybe fix this properly
const shouldSuppressError = patch.plugin === "ContextMenuAPI" && err instanceof SyntaxError && err.message.includes("arguments");
if (!shouldSuppressError) {
logger.error(`Patch by ${patch.plugin} errored (Module id is ${String(moduleId)}): ${replacement.match}\n`, err);
if (IS_COMPANION_TEST)
reporterData.failedPatches.erroredPatch.push({
...patch,
oldModule: lastCode,
newModule: code,
id: moduleId
});
if (IS_DEV) {
diffErroredPatch(code, lastCode, lastCode.match(replacement.match)!);
}
if (markedAsPatched) {
patchedBy.delete(patch.plugin);
}
if (patch.group) {
logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} errored`);
if (IS_COMPANION_TEST)
reporterData.failedPatches.undoingPatchGroup.push({
reporterData.failedPatches.erroredPatch.push({
...patch,
oldModule: lastCode,
newModule: code,
id: moduleId
});
code = previousCode;
patchedFactory = previousFactory;
break;
}
code = lastCode;
patchedFactory = lastFactory;
if (IS_DEV) {
diffErroredPatch(code, lastCode, lastCode.match(replacement.match)!);
}
if (markedAsPatched) {
patchedBy.delete(patch.plugin);
}
if (patch.group) {
logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} errored`);
if (IS_COMPANION_TEST)
reporterData.failedPatches.undoingPatchGroup.push({
...patch,
id: moduleId
});
code = previousCode;
patchedFactory = previousFactory;
break;
}
code = lastCode;
patchedFactory = lastFactory;
}
}
if (!patch.all) {
patches.splice(i--, 1);
}
}
if (!patch.all) {
patches.splice(i--, 1);
patchedFactory[SYM_ORIGINAL_FACTORY] = originalFactory;
if (IS_DEV && patchedFactory !== originalFactory) {
originalFactory[SYM_PATCHED_SOURCE] = patchedSource;
originalFactory[SYM_PATCHED_BY] = patchedBy;
}
return patchedFactory as PatchedModuleFactory;
}
patchedFactory[SYM_ORIGINAL_FACTORY] = originalFactory;
function diffErroredPatch(code: string, lastCode: string, match: RegExpMatchArray) {
const changeSize = code.length - lastCode.length;
if (IS_DEV && patchedFactory !== originalFactory) {
originalFactory[SYM_PATCHED_SOURCE] = patchedSource;
originalFactory[SYM_PATCHED_BY] = patchedBy;
// Use 200 surrounding characters of context
const start = Math.max(0, match.index! - 200);
const end = Math.min(lastCode.length, match.index! + match[0].length + 200);
// (changeSize may be negative)
const endPatched = end + changeSize;
const context = lastCode.slice(start, end);
const patchedContext = code.slice(start, endPatched);
// Inline require to avoid including it in !IS_DEV builds
const diff = (require("diff") as typeof import("diff")).diffWordsWithSpace(context, patchedContext);
let fmt = "%c %s ";
const elements: string[] = [];
for (const d of diff) {
const color = d.removed
? "red"
: d.added
? "lime"
: "grey";
fmt += "%c%s";
elements.push("color:" + color, d.value);
}
logger.errorCustomFmt(...Logger.makeTitle("white", "Before"), context);
logger.errorCustomFmt(...Logger.makeTitle("white", "After"), patchedContext);
const [titleFmt, ...titleElements] = Logger.makeTitle("white", "Diff");
logger.errorCustomFmt(titleFmt + fmt, ...titleElements, ...elements);
}
return patchedFactory as PatchedModuleFactory;
}
function diffErroredPatch(code: string, lastCode: string, match: RegExpMatchArray) {
const changeSize = code.length - lastCode.length;
// Use 200 surrounding characters of context
const start = Math.max(0, match.index! - 200);
const end = Math.min(lastCode.length, match.index! + match[0].length + 200);
// (changeSize may be negative)
const endPatched = end + changeSize;
const context = lastCode.slice(start, end);
const patchedContext = code.slice(start, endPatched);
// Inline require to avoid including it in !IS_DEV builds
const diff = (require("diff") as typeof import("diff")).diffWordsWithSpace(context, patchedContext);
let fmt = "%c %s ";
const elements: string[] = [];
for (const d of diff) {
const color = d.removed
? "red"
: d.added
? "lime"
: "grey";
fmt += "%c%s";
elements.push("color:" + color, d.value);
}
logger.errorCustomFmt(...Logger.makeTitle("white", "Before"), context);
logger.errorCustomFmt(...Logger.makeTitle("white", "After"), patchedContext);
const [titleFmt, ...titleElements] = Logger.makeTitle("white", "Diff");
logger.errorCustomFmt(titleFmt + fmt, ...titleElements, ...elements);
}