diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx index 93b1323e7..8ebe51a15 100644 --- a/src/components/Icons.tsx +++ b/src/components/Icons.tsx @@ -255,3 +255,21 @@ export function DeleteIcon(props: IconProps) { ); } + +/** + * A plugin icon, created by CorellanStoma. https://github.com/CreArts-Community/Settings-Icons + */ +export function PluginIcon(props: IconProps) { + return ( + + + + ); +} diff --git a/src/components/ThemeSettings/ThemesTab.tsx b/src/components/ThemeSettings/ThemesTab.tsx index 297fbfe34..e9543c209 100644 --- a/src/components/ThemeSettings/ThemesTab.tsx +++ b/src/components/ThemeSettings/ThemesTab.tsx @@ -21,7 +21,7 @@ import "./themesStyles.css"; import { Settings, useSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; import { Flex } from "@components/Flex"; -import { CogWheel, DeleteIcon } from "@components/Icons"; +import { CogWheel, DeleteIcon, PluginIcon } from "@components/Icons"; import { Link } from "@components/Link"; import { AddonCard } from "@components/VencordSettings/AddonCard"; import { SettingsTab, wrapTab } from "@components/VencordSettings/shared"; @@ -34,10 +34,11 @@ import type { ThemeHeader } from "@utils/themes"; import { getThemeInfo, stripBOM, type UserThemeHeader } from "@utils/themes/bd"; import { usercssParse } from "@utils/themes/usercss"; import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack"; -import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common"; -import type { ComponentType, Ref, SyntheticEvent } from "react"; +import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, Tooltip, useEffect, useMemo, useRef, useState } from "@webpack/common"; +import { type ComponentType, type Ref, type SyntheticEvent } from "react"; import type { UserstyleHeader } from "usercss-meta"; +import { isPluginEnabled } from "../../plugins"; import { UserCSSSettingsModal } from "./UserCSSModal"; type FileInput = ComponentType<{ @@ -118,6 +119,9 @@ interface UserCSSCardProps { } function UserCSSThemeCard({ theme, enabled, onChange, onDelete }: UserCSSCardProps) { + const missingPlugins = useMemo(() => + theme.requiredPlugins?.filter(p => !isPluginEnabled(p)), [theme]); + return ( + {missingPlugins && missingPlugins.length > 0 && ( + + {({ onMouseLeave, onMouseEnter }) => ( +
+ +
+ )} +
+ )} {theme.vars && (
openModal(modalProps => diff --git a/src/utils/themes/usercss/index.ts b/src/utils/themes/usercss/index.ts index a7d49a608..e4dbe2340 100644 --- a/src/utils/themes/usercss/index.ts +++ b/src/utils/themes/usercss/index.ts @@ -10,16 +10,22 @@ import { parse as originalParse, UserstyleHeader } from "usercss-meta"; const UserCSSLogger = new Logger("UserCSS", "#d2acf5"); export async function usercssParse(text: string, fileName: string): Promise { - var { metadata, errors } = originalParse(text.replace(/\r/g, ""), { allowErrors: true }); + const { metadata, errors } = originalParse(text.replace(/\r/g, ""), { + allowErrors: true, + unknownKey: "assign" + }); if (errors.length) { UserCSSLogger.warn("Parsed", fileName, "with errors:", errors); } + const requiredPlugins = metadata["vc-requiredPlugins"]?.split(",").map(p => p.trim()); + return { ...metadata, fileName, - id: await getUserCssId(metadata) + id: await getUserCssId(metadata), + requiredPlugins }; } diff --git a/src/utils/themes/usercss/usercss-meta.d.ts b/src/utils/themes/usercss/usercss-meta.d.ts index 1a39bf6d2..f4d29e631 100644 --- a/src/utils/themes/usercss/usercss-meta.d.ts +++ b/src/utils/themes/usercss/usercss-meta.d.ts @@ -55,6 +55,14 @@ declare module "usercss-meta" { */ fileName: string; + /** + * The required plugins for this style. + * + * @vencord Specific to Vencord, not part of the original module. + * @see {@link vc-requiredPlugins} + */ + requiredPlugins?: string[]; + /** * The name of your style. * @@ -107,7 +115,20 @@ declare module "usercss-meta" { * A list of variables the style defines. */ vars: Record; + + /** + * Required plugins for this style to work. Comma-separated list of plugin names. + * + * @vencord This is a Vencord-specific extension, however we wish for this to become a standard for client mods + * to implement, hence the more generic namespaced name. + */ + "vc-requiredPlugins"?: string; } - export function parse(text: string, options: { allowErrors: boolean; }): { metadata: UserstyleHeader; errors: { code: string; args: any; }[]; }; + type UserCSSParseOptions = { + allowErrors: boolean; + unknownKey: "assign"; + }; + + export function parse(text: string, options: UserCSSParseOptions): { metadata: UserstyleHeader; errors: { code: string; args: any; }[]; }; }