diff --git a/README.md b/README.md index cd54fcdc8..8611babd7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ The cutest Discord client mod -![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) +| ![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) | +|:--:| +| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | ## Features @@ -28,6 +30,14 @@ Visit https://vencord.dev/download https://discord.gg/D9uwnFnqmd +## Sponsors + +| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** | +|:--:| +| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) | +| *generated using [github-sponsor-graph](https://github.com/Vendicated/github-sponsor-graph)* | + + ## Star History diff --git a/package.json b/package.json index 393a4df71..cdcf49a56 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "typescript": "^5.0.4", "zip-local": "^0.3.5" }, - "packageManager": "pnpm@8.1.1", + "packageManager": "pnpm@8.10.2", "pnpm": { "patchedDependencies": { "eslint-plugin-path-alias@1.0.0": "patches/eslint-plugin-path-alias@1.0.0.patch", diff --git a/src/main/patchWin32Updater.ts b/src/main/patchWin32Updater.ts index ba7a9224e..96717a5ad 100644 --- a/src/main/patchWin32Updater.ts +++ b/src/main/patchWin32Updater.ts @@ -32,6 +32,8 @@ function isNewer($new: string, old: string) { } function patchLatest() { + if (process.env.DISABLE_UPDATER_AUTO_PATCHING) return; + try { const currentAppPath = dirname(process.execPath); const currentVersion = basename(currentAppPath); diff --git a/src/plugins/clientTheme/clientTheme.css b/src/plugins/clientTheme/clientTheme.css new file mode 100644 index 000000000..023f88bd2 --- /dev/null +++ b/src/plugins/clientTheme/clientTheme.css @@ -0,0 +1,24 @@ +.client-theme-settings { + display: flex; + flex-direction: column; +} + +.client-theme-container { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.client-theme-settings-labels { + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.client-theme-container > [class^="colorSwatch"] > [class^="swatch"] { + border: thin solid var(--background-modifier-accent) !important; +} + +.client-theme-warning { + color: var(--text-danger); +} diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx new file mode 100644 index 000000000..3e07b15fd --- /dev/null +++ b/src/plugins/clientTheme/index.tsx @@ -0,0 +1,228 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./clientTheme.css"; + +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import { getTheme, Theme } from "@utils/discord"; +import { Margins } from "@utils/margins"; +import { classes } from "@utils/misc"; +import definePlugin, { OptionType } from "@utils/types"; +import { findByCodeLazy } from "@webpack"; +import { Button, Forms } from "@webpack/common"; + +const ColorPicker = findByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR"); + +const colorPresets = [ + "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", + "#3A483D", "#344242", "#313D4B", "#2D2F47", "#322B42", + "#3C2E42", "#422938" +]; + +function onPickColor(color: number) { + let hexColor = color.toString(16); + + while (hexColor.length < 6) { + hexColor = "0" + hexColor; + } + + settings.store.color = hexColor; + updateColorVars(hexColor); +} + +function ThemeSettings() { + const lightnessWarning = hexToLightness(settings.store.color) > 45; + const lightModeWarning = getTheme() === Theme.Light; + + return ( +
+
+
+ Theme Color + Add a color to your Discord client theme +
+ +
+ {lightnessWarning || lightModeWarning + ?
+ + Your theme won't look good: + {lightnessWarning && Selected color is very light} + {lightModeWarning && Light mode isn't supported} +
+ : null} +
+ ); +} + +const settings = definePluginSettings({ + color: { + description: "Color your Discord client theme will be based around. Light mode isn't supported", + type: OptionType.COMPONENT, + default: "313338", + component: () => + }, + resetColor: { + description: "Reset Theme Color", + type: OptionType.COMPONENT, + default: "313338", + component: () => ( + + ) + } +}); + +export default definePlugin({ + name: "ClientTheme", + authors: [Devs.F53], + description: "Recreation of the old client theme experiment. Add a color to your Discord client theme", + settings, + + patches: [ + { + find: "Could not find app-mount", + replacement: { + match: /(?<=Could not find app-mount"\))/, + replace: ",$self.addThemeInitializer()" + } + } + ], + + addThemeInitializer() { + document.addEventListener("DOMContentLoaded", this.themeInitializer = () => { + updateColorVars(settings.store.color); + generateColorOffsets(); + }); + }, + + stop() { + document.removeEventListener("DOMContentLoaded", this.themeInitializer); + document.getElementById("clientThemeVars")?.remove(); + document.getElementById("clientThemeOffsets")?.remove(); + } +}); + +async function generateColorOffsets() { + const variableRegex = /(--primary-[5-9]\d{2}-hsl):.*?(\S*)%;/g; + + const styleLinkNodes = document.querySelectorAll('link[rel="stylesheet"]'); + const variableLightness = {} as Record; + + // Search all stylesheets for color variables + for (const styleLinkNode of styleLinkNodes) { + const cssLink = styleLinkNode.getAttribute("href"); + if (!cssLink) continue; + + const res = await fetch(cssLink); + const cssString = await res.text(); + + // Get lightness values of --primary variables >=500 + let variableMatch = variableRegex.exec(cssString); + while (variableMatch !== null) { + const [, variable, lightness] = variableMatch; + variableLightness[variable] = parseFloat(lightness); + variableMatch = variableRegex.exec(cssString); + } + } + + // Generate offsets + const lightnessOffsets = Object.entries(variableLightness) + .map(([key, lightness]) => { + const lightnessOffset = lightness - variableLightness["--primary-600-hsl"]; + const plusOrMinus = lightnessOffset >= 0 ? "+" : "-"; + return `${key}: var(--theme-h) var(--theme-s) calc(var(--theme-l) ${plusOrMinus} ${Math.abs(lightnessOffset).toFixed(2)}%);`; + }) + .join("\n"); + + const style = document.createElement("style"); + style.setAttribute("id", "clientThemeOffsets"); + style.textContent = `:root:root { + ${lightnessOffsets} + }`; + document.head.appendChild(style); +} + +function updateColorVars(color: string) { + const { hue, saturation, lightness } = hexToHSL(color); + + let style = document.getElementById("clientThemeVars"); + if (!style) { + style = document.createElement("style"); + style.setAttribute("id", "clientThemeVars"); + document.head.appendChild(style); + } + + style.textContent = `:root { + --theme-h: ${hue}; + --theme-s: ${saturation}%; + --theme-l: ${lightness}%; + }`; +} + +// https://css-tricks.com/converting-color-spaces-in-javascript/ +function hexToHSL(hexCode: string) { + // Hex => RGB normalized to 0-1 + const r = parseInt(hexCode.substring(0, 2), 16) / 255; + const g = parseInt(hexCode.substring(2, 4), 16) / 255; + const b = parseInt(hexCode.substring(4, 6), 16) / 255; + + // RGB => HSL + const cMax = Math.max(r, g, b); + const cMin = Math.min(r, g, b); + const delta = cMax - cMin; + + let hue: number, saturation: number, lightness: number; + + lightness = (cMax + cMin) / 2; + + if (delta === 0) { + // If r=g=b then the only thing that matters is lightness + hue = 0; + saturation = 0; + } else { + // Magic + saturation = delta / (1 - Math.abs(2 * lightness - 1)); + + if (cMax === r) + hue = ((g - b) / delta) % 6; + else if (cMax === g) + hue = (b - r) / delta + 2; + else + hue = (r - g) / delta + 4; + hue *= 60; + if (hue < 0) + hue += 360; + } + + // Move saturation and lightness from 0-1 to 0-100 + saturation *= 100; + lightness *= 100; + + return { hue, saturation, lightness }; +} + +// Minimized math just for lightness, lowers lag when changing colors +function hexToLightness(hexCode) { + // Hex => RGB normalized to 0-1 + const r = parseInt(hexCode.substring(0, 2), 16) / 255; + const g = parseInt(hexCode.substring(2, 4), 16) / 255; + const b = parseInt(hexCode.substring(4, 6), 16) / 255; + + const cMax = Math.max(r, g, b); + const cMin = Math.min(r, g, b); + + const lightness = 100 * ((cMax + cMin) / 2); + + return lightness; +} diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index f6b9ef952..52cf1d530 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -50,7 +50,7 @@ async function embedDidMount(this: Component) { const { titles, thumbnails } = await res.json(); const hasTitle = titles[0]?.votes >= 0; - const hasThumb = thumbnails[0]?.votes >= 0; + const hasThumb = thumbnails[0]?.votes >= 0 && !thumbnails[0].original; if (!hasTitle && !hasThumb) return; @@ -58,12 +58,12 @@ async function embedDidMount(this: Component) { enabled: true }; - if (titles[0]?.votes >= 0) { + if (hasTitle) { embed.dearrow.oldTitle = embed.rawTitle; embed.rawTitle = titles[0].title; } - if (thumbnails[0]?.votes >= 0 && thumbnails[0].timestamp) { + if (hasThumb) { embed.dearrow.oldThumb = embed.thumbnail.proxyURL; embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`; } diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 89fcf33e7..f8ea4b04a 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -77,15 +77,6 @@ export default definePlugin({ } ] }, - // Fix search history being disabled / broken with isStaff - { - find: '("showNewSearch")', - predicate: () => settings.store.enableIsStaff, - replacement: { - match: /(?<=showNewSearch"\);return)\s?/, - replace: "!1&&" - } - }, { find: 'H1,title:"Experiments"', replacement: { diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index 0b42a8b0b..4e747f363 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -8,7 +8,6 @@ import * as DataStore from "@api/DataStore"; import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; -import { useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; import { findStoreLazy } from "@webpack"; import { StatusSettingsStores, Tooltip } from "webpack/common"; @@ -27,14 +26,12 @@ interface IgnoredActivity { const RunningGameStore = findStoreLazy("RunningGameStore"); function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) { - const forceUpdate = useForceUpdater(); - return ( {tooltipProps => (