diff --git a/src/components/settings/tabs/sync/BackupAndRestoreTab.tsx b/src/components/settings/tabs/sync/BackupAndRestoreTab.tsx
index 53d8673e0..312dc204b 100644
--- a/src/components/settings/tabs/sync/BackupAndRestoreTab.tsx
+++ b/src/components/settings/tabs/sync/BackupAndRestoreTab.tsx
@@ -27,7 +27,7 @@ import { Margins } from "@utils/margins";
function BackupAndRestoreTab() {
return (
-
+
Importing a settings file will overwrite your current settings.
diff --git a/src/components/settings/tabs/sync/CloudTab.tsx b/src/components/settings/tabs/sync/CloudTab.tsx
index ad77e4bdf..59cac394e 100644
--- a/src/components/settings/tabs/sync/CloudTab.tsx
+++ b/src/components/settings/tabs/sync/CloudTab.tsx
@@ -109,7 +109,7 @@ function CloudTab() {
}
return (
-
+
Cloud Settings
@@ -136,7 +136,7 @@ function CloudTab() {
settings.cloud.authenticated = v;
}}
/>
- Backend URL
+ Backend URL
Which backend to use when using cloud integrations.
diff --git a/src/components/settings/tabs/updater/index.tsx b/src/components/settings/tabs/updater/index.tsx
index 29c63f864..47c184e93 100644
--- a/src/components/settings/tabs/updater/index.tsx
+++ b/src/components/settings/tabs/updater/index.tsx
@@ -183,7 +183,7 @@ function Updater() {
};
return (
-
+
Updater Settings
+
{(isEquicordDonor(user?.id) || isVencordDonor(user?.id)) ? (
;
+let LayoutTypes = {
+ SECTION: 1,
+ SIDEBAR_ITEM: 2,
+ PANEL: 3,
+ PANE: 4
+};
+waitFor(["SECTION", "SIDEBAR_ITEM", "PANEL", "PANE"], v => LayoutTypes = v);
+
+const FallbackSectionTypes = {
+ HEADER: "HEADER",
+ DIVIDER: "DIVIDER",
+ CUSTOM: "CUSTOM"
+};
+type SectionTypes = typeof FallbackSectionTypes;
+
+type SettingsLocation =
+ | "top"
+ | "aboveNitro"
+ | "belowNitro"
+ | "aboveActivity"
+ | "belowActivity"
+ | "bottom";
+
+interface SettingsLayoutNode {
+ type: number;
+ key?: string;
+ legacySearchKey?: string;
+ useLabel?(): string;
+ useTitle?(): string;
+ buildLayout?(): SettingsLayoutNode[];
+ icon?(): ReactNode;
+ render?(): ReactNode;
+}
+
+interface EntryOptions {
+ key: string,
+ title: string,
+ panelTitle?: string,
+ Component: ComponentType<{}>,
+ Icon: ComponentType;
+}
+interface SettingsLayoutBuilder {
+ key?: string;
+ buildLayout(): SettingsLayoutNode[];
+}
+
+const settings = definePluginSettings({
+ 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" },
+ ] as { label: string; value: SettingsLocation; default?: boolean; }[]
+ }
+});
+
+export function buildEntry(options: EntryOptions): SettingsLayoutNode {
+ const { key, title, panelTitle = title, Component, Icon } = options;
+
+ return ({
+ key,
+ type: LayoutTypes.SIDEBAR_ITEM,
+ legacySearchKey: title.toUpperCase(),
+ useTitle: () => title,
+ icon: () => ,
+ buildLayout: () => [
+ {
+ key: key + "_panel",
+ type: LayoutTypes.PANEL,
+ useTitle: () => panelTitle,
+ buildLayout: () => [
+ {
+ key: key + "_pane",
+ type: LayoutTypes.PANE,
+ buildLayout: () => [],
+ render: () => ,
+ useTitle: () => panelTitle
+ }
+ ]
+ }
+ ]
+ });
+}
export default definePlugin({
name: "Settings",
@@ -30,22 +120,31 @@ export default definePlugin({
authors: [Devs.Ven, Devs.Megu],
required: true,
+ settings,
+
patches: [
{
find: ".versionHash",
replacement: [
+ {
+ match: /\.compactInfo.+?(?=null!=(\i)&&(.{0,20}\i\.Text.{0,200}?,children:).{0,15}?("span"),({className:\i\.versionHash,children:\["Build Override: ",\1\.id\]\})\)\}\))/,
+ replace: (m, _buildOverride, makeRow, component, props) => {
+ props = props.replace(/children:\[.+\]/, "");
+ return `${m},$self.makeInfoElements(${component},${props}).map(e=>${makeRow}e})),`;
+ }
+ },
{
match: /\.info.+?\[\(0,\i\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/,
replace: (m, component, props) => {
props = props.replace(/children:\[.+\]/, "");
- return `${m},$self.makeInfoElements(${component}, ${props})`;
- },
+ return `${m},$self.makeInfoElements(${component},${props})`;
+ }
},
{
- match: /copyValue:\i\.join\(" "\)/,
- replace: "$& + $self.getInfoString()",
- },
- ],
+ match: /copyValue:\i\.join\(" "\)/g,
+ replace: "$& + $self.getInfoString()"
+ }
+ ]
},
{
find: ".SEARCH_NO_RESULTS&&0===",
@@ -78,15 +177,117 @@ export default definePlugin({
}
},
{
- find: "2025-09-user-settings-redesign-1",
+ find: ".buildLayout().map",
replacement: {
- match: /enabled:![01],showLegacyOpen:/g,
- replace: "enabled:false,showLegacyOpen:"
+ match: /(\i)\.buildLayout\(\)(?=\.map)/,
+ replace: "$self.buildLayout($1)"
}
}
],
+ buildLayout(originalLayoutBuilder: SettingsLayoutBuilder) {
+ const layout = originalLayoutBuilder.buildLayout();
+ if (originalLayoutBuilder.key !== "$Root") return layout;
+ if (!Array.isArray(layout)) return layout;
+
+ if (layout.some(s => s?.key === "vencord_section")) return layout;
+
+ const vencordEntries: SettingsLayoutNode[] = [
+ buildEntry({
+ key: "vencord_main",
+ title: "Equicord",
+ panelTitle: "Equicord Settings",
+ Component: VencordTab,
+ Icon: MainSettingsIcon
+ }),
+ buildEntry({
+ key: "vencord_plugins",
+ title: "Plugins",
+ Component: PluginsTab,
+ Icon: PluginsIcon
+ }),
+ buildEntry({
+ key: "vencord_themes",
+ title: "Themes",
+ Component: ThemesTab,
+ Icon: PaintbrushIcon
+ }),
+ !IS_UPDATER_DISABLED && UpdaterTab && buildEntry({
+ key: "vencord_updater",
+ title: "Updater",
+ panelTitle: "Equicord Updater",
+ Component: UpdaterTab,
+ Icon: UpdaterIcon
+ }),
+ buildEntry({
+ key: "vencord_cloud",
+ title: "Cloud",
+ panelTitle: "Equicord Cloud",
+ Component: CloudTab,
+ Icon: CloudIcon
+ }),
+ buildEntry({
+ key: "vencord_backup_restore",
+ title: "Backup & Restore",
+ Component: BackupAndRestoreTab,
+ Icon: BackupRestoreIcon
+ }),
+ IS_DEV && PatchHelperTab && buildEntry({
+ key: "vencord_patch_helper",
+ title: "Patch Helper",
+ Component: PatchHelperTab,
+ Icon: PatchHelperIcon
+ }),
+ ...this.customEntries.map(buildEntry),
+ // TODO: Remove deprecated customSections in a future update
+ ...this.customSections.map((func, i) => {
+ const { section, element, label } = func(FallbackSectionTypes);
+ if (Object.values(FallbackSectionTypes).includes(section)) return null;
+
+ return buildEntry({
+ key: `vencord_deprecated_custom_${section}`,
+ title: label,
+ Component: element,
+ Icon: section === "Vesktop" ? VesktopSettingsIcon : PlaceholderIcon
+ });
+ })
+ ].filter(isTruthy);
+
+ const vencordSection: SettingsLayoutNode = {
+ key: "vencord_section",
+ type: LayoutTypes.SECTION,
+ useLabel: () => "Vencord",
+ buildLayout: () => vencordEntries
+ };
+
+ const { settingsLocation } = settings.store;
+
+ const places: Record = {
+ top: "user_section",
+ aboveNitro: "billing_section",
+ belowNitro: "billing_section",
+ aboveActivity: "activity_section",
+ belowActivity: "activity_section",
+ bottom: "logout_section"
+ };
+
+ const key = places[settingsLocation] ?? places.top;
+ let idx = layout.findIndex(s => typeof s?.key === "string" && s.key === key);
+
+ if (idx === -1) {
+ idx = 2;
+ } else if (settingsLocation.startsWith("below")) {
+ idx += 1;
+ }
+
+ layout.splice(idx, 0, vencordSection);
+
+ return layout;
+ },
+
+ /** @deprecated Use customEntries */
customSections: [] as ((SectionTypes: SectionTypes) => any)[],
+ customEntries: [] as EntryOptions[],
makeSettingsCategories(SectionTypes: SectionTypes) {
return [
@@ -157,19 +358,13 @@ export default definePlugin({
].filter(Boolean);
},
- isRightSpot({
- header,
- settings,
- }: {
- header?: string;
- settings?: string[];
- }) {
- const firstChild = settings?.[0];
+ isRightSpot({ header, settings: s }: { header?: string; settings?: string[]; }) {
+ const firstChild = s?.[0];
// lowest two elements... sanity backup
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS")
return true;
- const { settingsLocation } = Settings.plugins.Settings;
+ const { settingsLocation } = settings.store;
if (settingsLocation === "bottom") return firstChild === "LOGOUT";
if (settingsLocation === "belowActivity")
@@ -178,7 +373,7 @@ export default definePlugin({
if (!header) return;
try {
- const names = {
+ const names: Record, string> = {
top: getIntlMessage("USER_SETTINGS"),
aboveNitro: getIntlMessage("BILLING_SETTINGS"),
belowNitro: getIntlMessage("APP_SETTINGS"),
@@ -218,33 +413,12 @@ export default definePlugin({
return (...args: any[]) => {
const elements = originalHook(...args);
if (!this.patchedSettings.has(elements))
- elements.unshift(
- ...this.makeSettingsCategories({
- HEADER: "HEADER",
- DIVIDER: "DIVIDER",
- CUSTOM: "CUSTOM",
- }),
- );
+ elements.unshift(...this.makeSettingsCategories(FallbackSectionTypes));
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 ||
@@ -298,8 +472,8 @@ export default definePlugin({
},
makeInfoElements(
- Component: React.ComponentType,
- props: React.PropsWithChildren,
+ Component: ComponentType,
+ props: PropsWithChildren,
) {
return this.getInfoRows().map((text, i) => (
diff --git a/src/plugins/lastfmRichPresence/index.tsx b/src/plugins/lastfmRichPresence/index.tsx
index ef32139ba..b7d448a68 100644
--- a/src/plugins/lastfmRichPresence/index.tsx
+++ b/src/plugins/lastfmRichPresence/index.tsx
@@ -251,7 +251,7 @@ export default definePlugin({
async getActivity(): Promise {
if (settings.store.hideWithActivity) {
- if (PresenceStore.getActivities(AuthenticationStore.getId()).some(a => a.application_id !== DISCORD_APP_ID)) {
+ if (PresenceStore.getActivities(AuthenticationStore.getId()).some(a => a.application_id !== DISCORD_APP_ID && a.type !== ActivityType.CUSTOM_STATUS)) {
return null;
}
}
diff --git a/src/plugins/previewMessage/index.tsx b/src/plugins/previewMessage/index.tsx
index 9b6c9e82b..aa52a5061 100644
--- a/src/plugins/previewMessage/index.tsx
+++ b/src/plugins/previewMessage/index.tsx
@@ -22,7 +22,7 @@ import { Devs } from "@utils/constants";
import definePlugin, { IconComponent, StartAt } from "@utils/types";
import { CloudUpload, MessageAttachment } from "@vencord/discord-types";
import { findByPropsLazy } from "@webpack";
-import { DraftStore, DraftType, SelectedChannelStore, UserStore, useStateFromStores } from "@webpack/common";
+import { DraftStore, DraftType, UserStore, useStateFromStores } from "@webpack/common";
const UploadStore = findByPropsLazy("getUploads");
@@ -89,11 +89,10 @@ const PreviewIcon: IconComponent = ({ height = 20, width = 20, className }) => {
);
};
-const PreviewButton: ChatBarButtonFactory = ({ isMainChat, isEmpty, type: { attachments } }) => {
- const channelId = SelectedChannelStore.getChannelId();
+const PreviewButton: ChatBarButtonFactory = ({ isAnyChat, isEmpty, type: { attachments }, channel: { id: channelId } }) => {
const draft = useStateFromStores([DraftStore], () => getDraft(channelId));
- if (!isMainChat) return null;
+ if (!isAnyChat) return null;
const hasAttachments = attachments && UploadStore.getUploads(channelId, DraftType.ChannelMessage).length > 0;
const hasContent = !isEmpty && draft?.length > 0;
diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx
index c5d047179..3ce306fef 100644
--- a/src/plugins/roleColorEverywhere/index.tsx
+++ b/src/plugins/roleColorEverywhere/index.tsx
@@ -135,7 +135,7 @@ export default definePlugin({
},
// Reaction List
{
- find: ".tagFaded:null",
+ find: ".reactionDefault",
replacement: {
match: /tag:"strong"(?=.{0,50}\i\.name)(?<=onContextMenu:.{0,15}\((\i),(\i),\i\).+?)/,
replace: "$&,style:$self.getColorStyle($2?.id,$1?.channel?.id)"
diff --git a/src/plugins/sendTimestamps/index.tsx b/src/plugins/sendTimestamps/index.tsx
index 531248287..bf93480d0 100644
--- a/src/plugins/sendTimestamps/index.tsx
+++ b/src/plugins/sendTimestamps/index.tsx
@@ -50,7 +50,7 @@ function parseTime(time: string) {
return ``;
}
-const Formats = ["", "t", "T", "d", "D", "f", "F", "R"] as const;
+const Formats = ["", "t", "T", "d", "D", "f", "F", "s", "S", "R"] as const;
type Format = typeof Formats[number];
const cl = classNameFactory("vc-st-");
@@ -146,8 +146,8 @@ const SendTimestampIcon: IconComponent = ({ height = 20, width = 20, className }
);
};
-const SendTimestampButton: ChatBarButtonFactory = ({ isMainChat }) => {
- if (!isMainChat) return null;
+const SendTimestampButton: ChatBarButtonFactory = ({ isAnyChat }) => {
+ if (!isAnyChat) return null;
return (
`https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/tm-grammars/grammars/${name}.json`;
+export const JSON_REPO = "Vencord/ShikiPluginAssets";
+export const JSON_REPO_COMMIT = "75d69df9fdf596a31eef8b7f6f891231a6feab44";
+export const JSON_URL = `https://cdn.jsdelivr.net/gh/${JSON_REPO}@${JSON_REPO_COMMIT}/grammars.json`;
+export const shikiRepoGrammar = (name: string) => `https://cdn.jsdelivr.net/gh/${SHIKI_REPO}@${SHIKI_REPO_COMMIT}/packages/tm-grammars/grammars/${name}.json`;
export interface Language {
name: string;
@@ -38,7 +38,6 @@ export interface Language {
export interface LanguageJson {
name: string;
displayName: string;
- fileName: string;
scopeName: string;
devicon?: string;
aliases?: string[];
diff --git a/src/plugins/shikiCodeblocks.desktop/api/themes.ts b/src/plugins/shikiCodeblocks.desktop/api/themes.ts
index 5859da998..4c2f8b682 100644
--- a/src/plugins/shikiCodeblocks.desktop/api/themes.ts
+++ b/src/plugins/shikiCodeblocks.desktop/api/themes.ts
@@ -19,8 +19,8 @@
import { IShikiTheme } from "@vap/shiki";
export const SHIKI_REPO = "shikijs/textmate-grammars-themes";
-export const SHIKI_REPO_COMMIT = "ecd9fb1b4a38381df95048c19cf9b8bdcbb1ec09";
-export const shikiRepoTheme = (name: string) => `https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/tm-themes/themes/${name}.json`;
+export const SHIKI_REPO_COMMIT = "bc5436518111d87ea58eb56d97b3f9bec30e6b83";
+export const shikiRepoTheme = (name: string) => `https://cdn.jsdelivr.net/gh/${SHIKI_REPO}@${SHIKI_REPO_COMMIT}/packages/tm-themes/themes/${name}.json`;
export const themes = {
// Default
diff --git a/src/plugins/translate/TranslateIcon.tsx b/src/plugins/translate/TranslateIcon.tsx
index 4bd52bc56..ed623328e 100644
--- a/src/plugins/translate/TranslateIcon.tsx
+++ b/src/plugins/translate/TranslateIcon.tsx
@@ -43,7 +43,7 @@ export const TranslateIcon: IconComponent = ({ height = 20, width = 20, classNam
export let setShouldShowTranslateEnabledTooltip: undefined | ((show: boolean) => void);
export const TranslateChatBarIcon: ChatBarButtonFactory = ({ isMainChat }) => {
- const { autoTranslate, showChatBarButton } = settings.use(["autoTranslate", "showChatBarButton"]);
+ const { autoTranslate } = settings.use(["autoTranslate"]);
const [shouldShowTranslateEnabledTooltip, setter] = useState(false);
useEffect(() => {
@@ -51,7 +51,7 @@ export const TranslateChatBarIcon: ChatBarButtonFactory = ({ isMainChat }) => {
return () => setShouldShowTranslateEnabledTooltip = undefined;
}, []);
- if (!isMainChat || !showChatBarButton) return null;
+ if (!isMainChat) return null;
const toggle = () => {
const newState = !autoTranslate;
diff --git a/src/plugins/translate/settings.ts b/src/plugins/translate/settings.ts
index 58042c5eb..5ab3883a4 100644
--- a/src/plugins/translate/settings.ts
+++ b/src/plugins/translate/settings.ts
@@ -45,11 +45,6 @@ export const settings = definePluginSettings({
hidden: true
},
- showChatBarButton: {
- type: OptionType.BOOLEAN,
- description: "Show translate button in chat bar",
- default: true
- },
service: {
type: OptionType.SELECT,
description: IS_WEB ? "Translation service (Not supported on Web!)" : "Translation service",
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 78010ea6c..bdedaf1e8 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -633,6 +633,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "alfred",
id: 1038466644353232967n
},
+ vv: {
+ name: "VV",
+ id: 254866377087778816n
+ },
} satisfies Record);
export const EquicordDevs = Object.freeze({