-
+
diff --git a/src/components/PluginSettings/styles.css b/src/components/PluginSettings/styles.css
index a4f9aeee..ed5e9aa1 100644
--- a/src/components/PluginSettings/styles.css
+++ b/src/components/PluginSettings/styles.css
@@ -70,7 +70,7 @@
padding: 1em;
background: var(--info-warning-background);
border: 1px solid var(--info-warning-foreground);
- color: var(--info-warning-text);
+ color: var(--info-warning-foreground);
}
.vc-plugins-restart-button {
diff --git a/src/components/VencordSettings/CloudTab.tsx b/src/components/VencordSettings/CloudTab.tsx
index a13c3f6c..809d4062 100644
--- a/src/components/VencordSettings/CloudTab.tsx
+++ b/src/components/VencordSettings/CloudTab.tsx
@@ -21,7 +21,7 @@ import { Settings, useSettings } from "@api/Settings";
import { CheckedTextInput } from "@components/CheckedTextInput";
import { Grid } from "@components/Grid";
import { Link } from "@components/Link";
-import { authorizeCloud, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud";
+import { authorizeCloud, checkCloudUrlCsp, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud";
import { Margins } from "@utils/margins";
import { deleteCloudSettings, getCloudSettings, putCloudSettings } from "@utils/settingsSync";
import { Alerts, Button, Forms, Switch, Tooltip } from "@webpack/common";
@@ -38,6 +38,8 @@ function validateUrl(url: string) {
}
async function eraseAllData() {
+ if (!await checkCloudUrlCsp()) return;
+
const res = await fetch(new URL("/v1/", getCloudUrl()), {
method: "DELETE",
headers: { Authorization: await getCloudAuth() }
diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx
index 55822069..accccd49 100644
--- a/src/components/VencordSettings/PatchHelperTab.tsx
+++ b/src/components/VencordSettings/PatchHelperTab.tsx
@@ -148,7 +148,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
)}
{compileResult &&
-
+
{compileResult[1]}
}
diff --git a/src/components/VencordSettings/ThemesTab.tsx b/src/components/VencordSettings/ThemesTab.tsx
index f718ab11..692e20bf 100644
--- a/src/components/VencordSettings/ThemesTab.tsx
+++ b/src/components/VencordSettings/ThemesTab.tsx
@@ -18,17 +18,21 @@
import { Settings, useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
+import { ErrorCard } from "@components/ErrorCard";
import { Flex } from "@components/Flex";
import { DeleteIcon, FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
import { Link } from "@components/Link";
import { openPluginModal } from "@components/PluginSettings/PluginModal";
import type { UserThemeHeader } from "@main/themes";
+import { CspBlockedUrls, useCspErrors } from "@utils/cspViolations";
import { openInviteModal } from "@utils/discord";
import { Margins } from "@utils/margins";
-import { showItemInFolder } from "@utils/native";
-import { useAwaiter } from "@utils/react";
+import { classes } from "@utils/misc";
+import { relaunch } from "@utils/native";
+import { useAwaiter, useForceUpdater } from "@utils/react";
+import { getStylusWebStoreUrl } from "@utils/web";
import { findLazy } from "@webpack";
-import { Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
+import { Alerts, Button, Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
import type { ComponentType, Ref, SyntheticEvent } from "react";
import Plugins from "~plugins";
@@ -65,7 +69,7 @@ function Validator({ link }: { link: string; }) {
: "Valid!";
return {text};
}
@@ -159,7 +163,6 @@ function ThemesTab() {
const [currentTab, setCurrentTab] = useState(ThemeTab.LOCAL);
const [themeText, setThemeText] = useState(settings.themeLinks.join("\n"));
const [userThemes, setUserThemes] = useState(null);
- const [themeDir, , themeDirPending] = useAwaiter(VencordNative.themes.getThemesDir);
useEffect(() => {
refreshLocalThemes();
@@ -219,6 +222,12 @@ function ThemesTab() {
If using the BD site, click on "Download" and place the downloaded .theme.css file into your themes folder.
+
+ External Resources
+ For security reasons, loading resources (styles, fonts, images, ...) from most sites is blocked.
+ Make sure all your assets are hosted on GitHub, GitLab, Codeberg, Imgur, Discord or Google Fonts.
+
+
<>
@@ -241,8 +250,7 @@ function ThemesTab() {
) : (
showItemInFolder(themeDir!)}
- disabled={themeDirPending}
+ action={() => VencordNative.themes.openFolder()}
Icon={FolderIcon}
/>
)}
@@ -347,10 +355,99 @@ function ThemesTab() {
+
{currentTab === ThemeTab.LOCAL && renderLocalThemes()}
{currentTab === ThemeTab.ONLINE && renderOnlineThemes()}
);
}
-export default wrapTab(ThemesTab, "Themes");
+export function CspErrorCard() {
+ if (IS_WEB) return null;
+
+ const errors = useCspErrors();
+ const forceUpdate = useForceUpdater();
+
+ if (!errors.length) return null;
+
+ const isImgurHtmlDomain = (url: string) => url.startsWith("https://imgur.com/");
+
+ const allowUrl = async (url: string) => {
+ const { origin: baseUrl, host } = new URL(url);
+
+ const result = await VencordNative.csp.requestAddOverride(baseUrl, ["connect-src", "img-src", "style-src", "font-src"], "Vencord Themes");
+ if (result !== "ok") return;
+
+ CspBlockedUrls.forEach(url => {
+ if (new URL(url).host === host) {
+ CspBlockedUrls.delete(url);
+ }
+ });
+
+ forceUpdate();
+
+ Alerts.show({
+ title: "Restart Required",
+ body: "A restart is required to apply this change",
+ confirmText: "Restart now",
+ cancelText: "Later!",
+ onConfirm: relaunch
+ });
+ };
+
+ const hasImgurHtmlDomain = errors.some(isImgurHtmlDomain);
+
+ return (
+
+ Blocked Resources
+ Some images, styles, or fonts were blocked because they come from disallowed domains.
+ It is highly recommended to move them to GitHub or Imgur. But you may also allow domains if you fully trust them.
+
+ After allowing a domain, you have to fully close (from tray / task manager) and restart {IS_DISCORD_DESKTOP ? "Discord" : "Vesktop"} to apply the change.
+
+
+ Blocked URLs
+
+ {errors.map((url, i) => (
+
+ {i !== 0 &&
}
+
+ {url}
+
+
+
+ ))}
+
+
+ {hasImgurHtmlDomain && (
+ <>
+
+
+ Imgur links should be direct links in the form of https://i.imgur.com/...
+
+ To obtain a direct link, right-click the image and select "Copy image address".
+ >
+ )}
+
+ );
+}
+
+function UserscriptThemesTab() {
+ return (
+
+
+ Themes are not supported on the Userscript!
+
+
+ You can instead install themes with the Stylus extension!
+
+
+
+ );
+}
+
+export default IS_USERSCRIPT
+ ? wrapTab(UserscriptThemesTab, "Themes")
+ : wrapTab(ThemesTab, "Themes");
diff --git a/src/components/VencordSettings/UpdaterTab.tsx b/src/components/VencordSettings/UpdaterTab.tsx
index e29d7dfd..9871bfcc 100644
--- a/src/components/VencordSettings/UpdaterTab.tsx
+++ b/src/components/VencordSettings/UpdaterTab.tsx
@@ -94,7 +94,7 @@ function Changes({ updates, repo, repoPending }: CommonProps & { updates: typeof
{message} - {author}
))}
diff --git a/src/components/VencordSettings/VencordTab.tsx b/src/components/VencordSettings/VencordTab.tsx
index 54da5a3f..047ba17d 100644
--- a/src/components/VencordSettings/VencordTab.tsx
+++ b/src/components/VencordSettings/VencordTab.tsx
@@ -26,8 +26,7 @@ import { gitRemote } from "@shared/vencordUserAgent";
import { DONOR_ROLE_ID, VENCORD_GUILD_ID } from "@utils/constants";
import { Margins } from "@utils/margins";
import { identity, isPluginDev } from "@utils/misc";
-import { relaunch, showItemInFolder } from "@utils/native";
-import { useAwaiter } from "@utils/react";
+import { relaunch } from "@utils/native";
import { Button, Forms, GuildMemberStore, React, Select, Switch, UserStore } from "@webpack/common";
import BadgeAPI from "../../plugins/_api/badges";
@@ -53,9 +52,6 @@ type KeysOfType