* add tone indicators plugin * im tired of this man * oh my fucking god its EquicordDevs not Devs * dont use json * Update index.tsx * Initial commit * past updates history, new plugins section, more git commit information. waho * move it to be under updater instead of cloud * LINT ERROR FIX PLEASE * wait why does it say 2022 * add support for new settings, history list scrollable and taller, data migration, debugging commands. * add support for required plugins, also name things logs instead of sessions, clean up code too * Fixes * holy changes. ability to bulk clear and specific clear logs with either a trash can icon or the "clear logs" button at the top of the page, plugin entries in the logs section showing if something is required of any other plugins, fetch logs from repository which checks for duplicate hash to prevent duplicate messy logs, fixed cramming of elements by extending the page itself, made things look better. * Fix Lint * Update styles.css * Remove NewPluginsManager * API Plugin Check --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> Co-authored-by: justjxke <Jake0300@protonmai.com>
310 lines
9.9 KiB
TypeScript
310 lines
9.9 KiB
TypeScript
/*
|
|
* Vencord, a Discord client mod
|
|
* Copyright (c) 2025 Vendicated and contributors
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
import { Settings } from "@api/Settings";
|
|
import {
|
|
BackupAndRestoreTab,
|
|
ChangelogTab,
|
|
CloudTab,
|
|
PatchHelperTab,
|
|
PluginsTab,
|
|
UpdaterTab,
|
|
VencordTab,
|
|
} from "@components/settings";
|
|
import ThemesTab from "@components/ThemeSettings/ThemesTab";
|
|
import { Devs } from "@utils/constants";
|
|
import { getIntlMessage } from "@utils/discord";
|
|
import definePlugin, { OptionType } from "@utils/types";
|
|
import { shortGitHash } from "@utils/updater";
|
|
import { React } from "@webpack/common";
|
|
|
|
type SectionType = "HEADER" | "DIVIDER" | "CUSTOM";
|
|
type SectionTypes = Record<SectionType, SectionType>;
|
|
|
|
export default definePlugin({
|
|
name: "Settings",
|
|
description: "Adds Settings UI and debug info",
|
|
authors: [Devs.Ven, Devs.Megu],
|
|
required: true,
|
|
|
|
patches: [
|
|
{
|
|
find: ".versionHash",
|
|
replacement: [
|
|
{
|
|
match: /\.info.+?\[\(0,\i\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/,
|
|
replace: (m, component, props) => {
|
|
props = props.replace(/children:\[.+\]/, "");
|
|
return `${m},$self.makeInfoElements(${component}, ${props})`;
|
|
},
|
|
},
|
|
{
|
|
match: /copyValue:\i\.join\(" "\)/,
|
|
replace: "$& + $self.getInfoString()",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
find: ".SEARCH_NO_RESULTS&&0===",
|
|
replacement: [
|
|
{
|
|
match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/,
|
|
replace: (
|
|
_,
|
|
sectionTypes,
|
|
commaOrSemi,
|
|
elements,
|
|
element,
|
|
) =>
|
|
`${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`,
|
|
},
|
|
{
|
|
match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}return \i\.useMemo\(\(\)=>\i\(\3).+?\(\)=>)\2/,
|
|
replace: (_, rest, settingsHook) =>
|
|
`${rest}$self.wrapSettingsHook(${settingsHook})`,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
find: ".DEVELOPER_SECTION,",
|
|
replacement: [
|
|
{
|
|
match: /\i\.\i\.isDeveloper/,
|
|
replace: "true"
|
|
},
|
|
]
|
|
},
|
|
{
|
|
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
|
replacement: {
|
|
match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/,
|
|
replace: "$2.open($1);return;",
|
|
},
|
|
},
|
|
],
|
|
|
|
customSections: [] as ((SectionTypes: SectionTypes) => any)[],
|
|
|
|
makeSettingsCategories(SectionTypes: SectionTypes) {
|
|
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: "settings/tabsSync",
|
|
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,
|
|
}: {
|
|
header?: string;
|
|
settings?: string[];
|
|
}) {
|
|
const firstChild = settings?.[0];
|
|
// lowest two elements... sanity backup
|
|
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS")
|
|
return true;
|
|
|
|
const { settingsLocation } = Settings.plugins.Settings;
|
|
|
|
if (settingsLocation === "bottom") return firstChild === "LOGOUT";
|
|
if (settingsLocation === "belowActivity")
|
|
return firstChild === "CHANGELOG";
|
|
|
|
if (!header) return;
|
|
|
|
try {
|
|
const names = {
|
|
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: SectionTypes,
|
|
) {
|
|
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: "HEADER",
|
|
DIVIDER: "DIVIDER",
|
|
CUSTOM: "CUSTOM",
|
|
}),
|
|
);
|
|
|
|
return elements;
|
|
};
|
|
},
|
|
|
|
options: {
|
|
settingsLocation: {
|
|
type: OptionType.SELECT,
|
|
description: "Where to put the Equicord settings section",
|
|
options: [
|
|
{ label: "At the very top", value: "top" },
|
|
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
|
|
{ label: "Below the Nitro section", value: "belowNitro" },
|
|
{ label: "Above Activity Settings", value: "aboveActivity" },
|
|
{ label: "Below Activity Settings", value: "belowActivity" },
|
|
{ label: "At the very bottom", value: "bottom" },
|
|
],
|
|
},
|
|
},
|
|
|
|
get electronVersion() {
|
|
return (
|
|
VencordNative.native.getVersions().electron ||
|
|
window.legcord?.electron ||
|
|
null
|
|
);
|
|
},
|
|
|
|
get chromiumVersion() {
|
|
try {
|
|
return (
|
|
VencordNative.native.getVersions().chrome ||
|
|
// @ts-expect-error Typescript will add userAgentData IMMEDIATELY
|
|
navigator.userAgentData?.brands?.find(
|
|
b =>
|
|
b.brand === "Chromium" || b.brand === "Google Chrome",
|
|
)?.version ||
|
|
null
|
|
);
|
|
} catch {
|
|
// inb4 some stupid browser throws unsupported error for navigator.userAgentData, it's only in chromium
|
|
return null;
|
|
}
|
|
},
|
|
|
|
getVersionInfo(support = true) {
|
|
let version = "";
|
|
|
|
if (IS_DEV) version = "Dev";
|
|
if (IS_WEB) version = "Web";
|
|
if (IS_VESKTOP) version = `Vesktop v${VesktopNative.app.getVersion()}`;
|
|
if (IS_EQUIBOP) version = `Equibop v${VesktopNative.app.getVersion()}`;
|
|
if (IS_STANDALONE) version = "Standalone";
|
|
|
|
return support && version ? ` (${version})` : version;
|
|
},
|
|
|
|
getInfoRows() {
|
|
const { electronVersion, chromiumVersion, getVersionInfo } = this;
|
|
|
|
const rows = [`Equicord ${shortGitHash()}${getVersionInfo()}`];
|
|
|
|
if (electronVersion) rows.push(`Electron ${electronVersion}`);
|
|
if (chromiumVersion) rows.push(`Chromium ${chromiumVersion}`);
|
|
|
|
return rows;
|
|
},
|
|
|
|
getInfoString() {
|
|
return "\n" + this.getInfoRows().join("\n");
|
|
},
|
|
|
|
makeInfoElements(
|
|
Component: React.ComponentType<React.PropsWithChildren>,
|
|
props: React.PropsWithChildren,
|
|
) {
|
|
return this.getInfoRows().map((text, i) => (
|
|
<Component key={i} {...props}>
|
|
{text}
|
|
</Component>
|
|
));
|
|
},
|
|
});
|