From bc9ecbd13f46e234990baeb9c99b587a3802d72d Mon Sep 17 00:00:00 2001 From: prism Date: Sun, 7 Dec 2025 21:28:03 +0100 Subject: [PATCH] UI related stuff (#545) * components changes * feat(UpdaterTab): redesign * feat(BackupTab): redesign * feat(PatchTab): minor changes * feat(ChangelogTab): minor changes * feat(EquicordTab): minor changes * t * L * bruh * t * framework * t * fixes * hell je * Revert "hell je" This reverts commit 3f3134249c690cb50887ed6ee371790e6d4fd262. * fixes * fix fix --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> --- src/api/SettingsSync/cloudSync.ts | 2 +- src/api/SettingsSync/offline.ts | 99 ++++--- src/components/BaseText.tsx | 35 ++- src/components/ErrorCard.css | 22 +- src/components/FormSwitch.css | 33 ++- src/components/FormSwitch.tsx | 28 +- src/components/Link.css | 14 + src/components/Link.tsx | 48 ++-- src/components/Span.tsx | 4 +- .../settings/tabs/changelog/index.tsx | 249 ++++++++---------- .../tabs/patchHelper/FullPatchInput.tsx | 4 - .../settings/tabs/patchHelper/index.tsx | 97 ++++--- .../tabs/sync/BackupAndRestoreTab.tsx | 135 ++++++---- .../settings/tabs/updater/Components.tsx | 109 ++++---- .../settings/tabs/updater/index.tsx | 163 ++---------- .../settings/tabs/vencord/index.tsx | 199 +++++++------- .../loginWithQR/ui/modals/VerifyModal.tsx | 4 +- src/plugins/showTimeoutDuration/index.tsx | 2 +- 18 files changed, 603 insertions(+), 644 deletions(-) create mode 100644 src/components/Link.css diff --git a/src/api/SettingsSync/cloudSync.ts b/src/api/SettingsSync/cloudSync.ts index 14c41f4a7..6efe46792 100644 --- a/src/api/SettingsSync/cloudSync.ts +++ b/src/api/SettingsSync/cloudSync.ts @@ -135,7 +135,7 @@ export async function getCloudSettings(shouldNotify = true, force = false) { const data = await res.arrayBuffer(); const settings = new TextDecoder().decode(inflateSync(new Uint8Array(data))); - await importSettings(settings); + await importSettings(settings, "all", true); // sync with server timestamp instead of local one PlainSettings.cloud.settingsSyncVersion = written; diff --git a/src/api/SettingsSync/offline.ts b/src/api/SettingsSync/offline.ts index e28786fac..2e0cbea9d 100644 --- a/src/api/SettingsSync/offline.ts +++ b/src/api/SettingsSync/offline.ts @@ -11,6 +11,8 @@ import { moment, Toasts } from "@webpack/common"; import { DataStore } from ".."; +type BackupType = "all" | "plugins" | "css" | "datastore"; + const toast = (type: string, message: string) => Toasts.show({ type, @@ -41,7 +43,7 @@ function isSafeObject(obj: any) { return true; } -export async function importSettings(data: string) { +export async function importSettings(data: string, type: BackupType = "all", cloud = false) { try { var parsed = JSON.parse(data); } catch (err) { @@ -52,29 +54,60 @@ export async function importSettings(data: string) { if (!isSafeObject(parsed)) throw new Error("Unsafe Settings"); - if ("settings" in parsed && "quickCss" in parsed) { - Object.assign(PlainSettings, parsed.settings); - await VencordNative.settings.set(parsed.settings); - await VencordNative.quickCss.set(parsed.quickCss); - if (parsed.dataStore) await DataStore.setMany(parsed.dataStore); - } else - throw new Error("Invalid Settings. Is this even a Vencord Settings file?"); + switch (type) { + case "all": { + if (!cloud && (!("settings" in parsed) || !("quickCss" in parsed) || !("dataStore" in parsed))) + throw new Error("Invalid Settings. Missing one or more required keys."); + + if (parsed.settings) { + Object.assign(PlainSettings, parsed.settings); + await VencordNative.settings.set(parsed.settings); + } + if (parsed.quickCss) await VencordNative.quickCss.set(parsed.quickCss); + if (parsed.dataStore) await DataStore.setMany(parsed.dataStore); + break; + } + case "plugins": { + if (!parsed.settings?.settings) throw new Error("Plugin settings missing"); + + Object.assign(PlainSettings, parsed.settings.settings); + await VencordNative.settings.set(parsed.settings.settings); + break; + } + case "css": { + if (!parsed.quickCss) throw new Error("CSS missing"); + + await VencordNative.quickCss.set(parsed.quickCss); + break; + } + case "datastore": { + if (!parsed.dataStore) throw new Error("DataStore data missing"); + + await DataStore.setMany(parsed.dataStore); + break; + } + } } -export async function exportSettings({ syncDataStore = true, minify }: { syncDataStore?: boolean; minify?: boolean; }) { +export async function exportSettings({ syncDataStore = true, type = "all", minify }: { syncDataStore?: boolean; type?: BackupType; minify?: boolean; }) { const settings = VencordNative.settings.get(); const quickCss = await VencordNative.quickCss.get(); const dataStore = syncDataStore ? await DataStore.entries() : undefined; - return JSON.stringify( - { - settings, - quickCss, - ...(syncDataStore && { dataStore }) - }, - null, - minify ? undefined : 4 - ); + switch (type) { + case "all": { + return JSON.stringify({ settings, quickCss, ...(syncDataStore && { dataStore }) }, null, minify ? undefined : 4); + } + case "plugins": { + return JSON.stringify({ settings: { settings } }, null, minify ? undefined : 4); + } + case "css": { + return JSON.stringify({ quickCss }, null, minify ? undefined : 4); + } + case "datastore": { + return JSON.stringify({ dataStore }, null, minify ? undefined : 4); + } + } } export async function exportPlugins({ minify }: { minify?: boolean; } = {}) { @@ -92,28 +125,8 @@ export async function exportDataStores({ minify }: { minify?: boolean; } = {}) { return JSON.stringify({ dataStore }, null, minify ? undefined : 4); } -type BackupType = "settings" | "plugins" | "css" | "datastore"; - -export async function downloadSettingsBackup(type: BackupType, { minify }: { minify?: boolean; } = {}) { - let backup: string; - - switch (type) { - case "settings": - backup = await exportSettings({ minify }); - break; - case "plugins": - backup = await exportPlugins({ minify }); - break; - case "css": - backup = await exportCSS({ minify }); - break; - case "datastore": - backup = await exportDataStores({ minify }); - break; - default: - throw new Error("Invalid backup type"); - } - +export async function downloadSettingsBackup(type: BackupType = "all", { minify }: { minify?: boolean; } = {}) { + const backup = await exportSettings({ minify, type }); const filename = `equicord-${type}-backup-${moment().format("YYYY-MM-DD")}.json`; const data = new TextEncoder().encode(backup); @@ -124,7 +137,7 @@ export async function downloadSettingsBackup(type: BackupType, { minify }: { min } } -export async function uploadSettingsBackup(showToast = true): Promise { +export async function uploadSettingsBackup(type: BackupType = "all", showToast = true): Promise { if (IS_DISCORD_DESKTOP) { const [file] = await DiscordNative.fileManager.openFiles({ filters: [ @@ -135,7 +148,7 @@ export async function uploadSettingsBackup(showToast = true): Promise { if (file) { try { - await importSettings(new TextDecoder().decode(file.data)); + await importSettings(new TextDecoder().decode(file.data), type); if (showToast) toastSuccess(); } catch (err) { logger.error(err); @@ -149,7 +162,7 @@ export async function uploadSettingsBackup(showToast = true): Promise { const reader = new FileReader(); reader.onload = async () => { try { - await importSettings(reader.result as string); + await importSettings(reader.result as string, type); if (showToast) toastSuccess(); } catch (err) { logger.error(err); diff --git a/src/components/BaseText.tsx b/src/components/BaseText.tsx index 3b0192eb4..b68692051 100644 --- a/src/components/BaseText.tsx +++ b/src/components/BaseText.tsx @@ -13,7 +13,7 @@ import type { ComponentPropsWithoutRef, ReactNode } from "react"; const textCls = classNameFactory("vc-text-"); -const Sizes = { +export const TextSizes = { xxs: "0.625rem", xs: "0.75rem", sm: "0.875rem", @@ -23,7 +23,7 @@ const Sizes = { xxl: "2rem" } as const; -const Weights = { +export const TextWeights = { thin: "100", extralight: "200", light: "300", @@ -34,27 +34,42 @@ const Weights = { extrabold: "800", } as const; +export const TextColors = { + "header-primary": "var(--header-primary)", + "text-default": "var(--text-default)", + "text-muted": "var(--text-muted)", + "text-link": "var(--text-link)", + "text-danger": "var(--text-feedback-critical)", + "text-brand": "var(--text-brand)", + "text-strong": "var(--text-strong)", + "text-subtle": "var(--text-subtle)", + "control-text-critical": "var(--control-text-critical-secondary-default)", + "control-text-primary": "var(--control-text-primary-default)", +} as const; + export function generateTextCss() { let css = ""; - for (const [size, value] of Object.entries(Sizes)) { + for (const [size, value] of Object.entries(TextSizes)) { css += `.${textCls(size)}{font-size:${value};}`; } - for (const [weight, value] of Object.entries(Weights)) { + for (const [weight, value] of Object.entries(TextWeights)) { css += `.${textCls(weight)}{font-weight:${value};}`; } return css; } -export type TextSize = keyof typeof Sizes; -export type TextWeight = keyof typeof Weights; +export type TextSize = keyof typeof TextSizes; +export type TextWeight = keyof typeof TextWeights; +export type TextColor = keyof typeof TextColors; export type TextTag = "div" | "span" | "p" | `h${1 | 2 | 3 | 4 | 5 | 6}`; export type BaseTextProps = ComponentPropsWithoutRef & { size?: TextSize; weight?: TextWeight; + color?: TextColor; tag?: Tag; }; @@ -62,6 +77,7 @@ export function BaseText(props: BaseTextProps): Re const { size = "md", weight = "normal", + color, tag: Tag = "div", children, className, @@ -72,7 +88,10 @@ export function BaseText(props: BaseTextProps): Re return ( {children} @@ -81,7 +100,7 @@ export function BaseText(props: BaseTextProps): Re } -// #region Old compability +// #region Old compatibility export const TextCompat: DiscordText = function TextCompat({ color, variant, ...restProps }) { const newBaseTextProps = restProps as BaseTextProps; diff --git a/src/components/ErrorCard.css b/src/components/ErrorCard.css index 0d92dfa2e..49ee6097f 100644 --- a/src/components/ErrorCard.css +++ b/src/components/ErrorCard.css @@ -1,11 +1,15 @@ .vc-error-card { - padding: 2em; - background-color: #e7828430; - border: 1px solid #e78284; - border-radius: 5px; - color: var(--text-default, white); + padding: 1em; + background-color: var(--info-danger-background, rgb(237 66 69 / 10%)); + border: 1px solid var(--info-danger-foreground, #ed4245); + border-radius: var(--radius-sm, 8px); + color: var(--text-default); +} - & a:hover { - text-decoration: underline; - } -} \ No newline at end of file +.vc-error-card a { + color: var(--text-link); +} + +.vc-error-card a:hover { + text-decoration: underline; +} diff --git a/src/components/FormSwitch.css b/src/components/FormSwitch.css index 129c58475..077bd772b 100644 --- a/src/components/FormSwitch.css +++ b/src/components/FormSwitch.css @@ -1,15 +1,20 @@ -.vc-form-switch-wrapper { +.vc-form-switch-container { + padding-bottom: 20px; margin-bottom: 20px; - cursor: pointer; + border-bottom: 1px solid var(--border-subtle); +} + +.vc-form-switch-no-border { + border-bottom: none; + margin-bottom: 0; } .vc-form-switch { display: flex; - width: 100%; - - > :last-child { - margin-left: auto; - } + align-items: flex-start; + justify-content: space-between; + cursor: pointer; + gap: 16px; } .vc-form-switch-disabled { @@ -19,12 +24,12 @@ } .vc-form-switch-text { - display: flex; - flex-direction: column; - justify-content: center; - gap: 8px; + flex: 1; + min-width: 0; + max-width: 80%; } -.vc-form-switch-border { - margin-top: 20px; -} \ No newline at end of file +.vc-form-switch-control { + flex-shrink: 0; + padding-top: 2px; +} diff --git a/src/components/FormSwitch.tsx b/src/components/FormSwitch.tsx index 255f77992..4a37032fb 100644 --- a/src/components/FormSwitch.tsx +++ b/src/components/FormSwitch.tsx @@ -9,7 +9,7 @@ import "./FormSwitch.css"; import { classes } from "@utils/misc"; import type { PropsWithChildren, ReactNode } from "react"; -import { Divider } from "./Divider"; +import { Flex } from "./Flex"; import { Span } from "./Span"; import { Switch } from "./Switch"; @@ -26,24 +26,22 @@ export interface FormSwitchProps { export function FormSwitch({ onChange, title, value, description, disabled, className, hideBorder }: FormSwitchProps) { return ( -