mirror of
https://github.com/Equicord/Equicord.git
synced 2025-02-20 15:18:50 -05:00
feat(DiscordColorways): Update
This commit is contained in:
parent
a6dd7fb963
commit
be8e970a9e
20 changed files with 2650 additions and 1307 deletions
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { DataStore } from "@api/index";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Button, Forms, Text, useState } from "@webpack/common";
|
||||
|
||||
import { getAutoPresets } from "../css";
|
||||
|
||||
export default function ({ modalProps, onChange, autoColorwayId = "" }: { modalProps: ModalProps, onChange: (autoPresetId: string) => void, autoColorwayId: string; }) {
|
||||
const [autoId, setAutoId] = useState(autoColorwayId);
|
||||
const { radioBar, item: radioBarItem, itemFilled: radioBarItemFilled, radioPositionLeft } = findByProps("radioBar");
|
||||
return <ModalRoot {...modalProps}>
|
||||
<ModalHeader>
|
||||
<Text variant="heading-lg/semibold" tag="h1">
|
||||
Auto Preset Settings
|
||||
</Text>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div className="dc-info-card" style={{ marginTop: "1em" }}>
|
||||
<strong>About the Auto Colorway</strong>
|
||||
<span>The auto colorway allows you to use your system's accent color in combination with a selection of presets that will fully utilize it.</span>
|
||||
</div>
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<Forms.FormTitle>Presets:</Forms.FormTitle>
|
||||
{Object.values(getAutoPresets()).map(autoPreset => {
|
||||
return <div className={`${radioBarItem} ${radioBarItemFilled}`} aria-checked={autoId === autoPreset.id}>
|
||||
<div
|
||||
className={`${radioBar} ${radioPositionLeft}`}
|
||||
style={{ padding: "10px" }}
|
||||
onClick={() => {
|
||||
setAutoId(autoPreset.id);
|
||||
}}>
|
||||
<svg aria-hidden="true" role="img" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
||||
{autoId === autoPreset.id && <circle cx="12" cy="12" r="5" className="radioIconForeground-3wH3aU" fill="currentColor" />}
|
||||
</svg>
|
||||
<Text variant="eyebrow" tag="h5">{autoPreset.name}</Text>
|
||||
</div>
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.BRAND_NEW}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
onClick={() => {
|
||||
DataStore.set("activeAutoPreset", autoId);
|
||||
onChange(autoId);
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={() => {
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>;
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Button, Forms, ScrollerThin, Switch, Text, useState } from "@webpack/common";
|
||||
|
||||
import { getPreset } from "../css";
|
||||
|
@ -13,27 +14,31 @@ export default function ({ modalProps, onSettings, presetId, hasTintedText, hasD
|
|||
const [tintedText, setTintedText] = useState<boolean>(hasTintedText);
|
||||
const [discordSaturation, setDiscordSaturation] = useState<boolean>(hasDiscordSaturation);
|
||||
const [preset, setPreset] = useState<string>(presetId);
|
||||
const { radioBar, item: radioBarItem, itemFilled: radioBarItemFilled, radioPositionLeft } = findByProps("radioBar");
|
||||
return <ModalRoot {...modalProps} className="colorwaysPresetPicker">
|
||||
<ModalHeader><Text variant="heading-lg/semibold" tag="h1">Creator Settings</Text></ModalHeader>
|
||||
<ModalContent className="colorwaysPresetPicker-content">
|
||||
<div className="colorwaysCreator-settingCat" style={{ marginBottom: "20px" }}>
|
||||
<Forms.FormTitle style={{ marginBottom: "0" }}>
|
||||
Presets:
|
||||
</Forms.FormTitle>
|
||||
<ScrollerThin orientation="vertical" className="colorwaysCreator-settingsList" paddingFix style={{ paddingRight: "2px" }}>
|
||||
{Object.values(getPreset()).map(pre => {
|
||||
return <div className="colorwaysCreator-settingItm colorwaysCreator-preset" onClick={() => {
|
||||
setPreset(pre.id);
|
||||
}}>
|
||||
<Forms.FormTitle>
|
||||
Presets:
|
||||
</Forms.FormTitle>
|
||||
<ScrollerThin orientation="vertical" paddingFix style={{ paddingRight: "2px", marginBottom: "20px", maxHeight: "250px" }}>
|
||||
{Object.values(getPreset()).map(pre => {
|
||||
return <div className={`${radioBarItem} ${radioBarItemFilled}`} aria-checked={preset === pre.id}>
|
||||
<div
|
||||
className={`${radioBar} ${radioPositionLeft}`}
|
||||
style={{ padding: "10px" }}
|
||||
onClick={() => {
|
||||
setPreset(pre.id);
|
||||
}}>
|
||||
<svg aria-hidden="true" role="img" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
||||
{preset === pre.id && <circle cx="12" cy="12" r="5" className="radioIconForeground-3wH3aU" fill="currentColor" />}
|
||||
</svg>
|
||||
<Text variant="eyebrow" tag="h5">{pre.name}</Text>
|
||||
</div>;
|
||||
})}
|
||||
</ScrollerThin>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
})}
|
||||
</ScrollerThin>
|
||||
<Switch value={tintedText} onChange={setTintedText}>Use colored text</Switch>
|
||||
<Switch value={discordSaturation} onChange={setDiscordSaturation} hideBorder style={{ marginBottom: "0" }}>Use Discord's saturation</Switch>
|
||||
</ModalContent>
|
||||
|
|
|
@ -9,33 +9,22 @@ import { openModal } from "@utils/modal";
|
|||
import { FluxDispatcher, Text, Tooltip, useEffect, useState } from "@webpack/common";
|
||||
import { FluxEvents } from "@webpack/types";
|
||||
|
||||
import { getAutoPresets } from "../css";
|
||||
import { ColorwayObject } from "../types";
|
||||
import { PalleteIcon } from "./Icons";
|
||||
import Selector from "./Selector";
|
||||
|
||||
export default function ({
|
||||
listItemClass = "ColorwaySelectorBtnContainer",
|
||||
listItemWrapperClass = "",
|
||||
listItemTooltipClass = "colorwaysBtn-tooltipContent"
|
||||
}: {
|
||||
listItemClass?: string;
|
||||
listItemWrapperClass?: string;
|
||||
listItemTooltipClass?: string;
|
||||
}) {
|
||||
export default function () {
|
||||
const [activeColorway, setActiveColorway] = useState<string>("None");
|
||||
const [visibility, setVisibility] = useState<boolean>(true);
|
||||
const [isThin, setIsThin] = useState<boolean>(false);
|
||||
async function setButtonVisibility() {
|
||||
const [showColorwaysButton, useThinMenuButton] = await DataStore.getMany([
|
||||
"showColorwaysButton",
|
||||
"useThinMenuButton"
|
||||
]);
|
||||
|
||||
setVisibility(showColorwaysButton);
|
||||
setIsThin(useThinMenuButton);
|
||||
}
|
||||
|
||||
const [autoPreset, setAutoPreset] = useState<string>("hueRotation");
|
||||
useEffect(() => {
|
||||
setButtonVisibility();
|
||||
(async function () {
|
||||
setVisibility(await DataStore.get("showColorwaysButton") as boolean);
|
||||
setIsThin(await DataStore.get("useThinMenuButton") as boolean);
|
||||
setAutoPreset(await DataStore.get("activeAutoPreset") as string);
|
||||
})();
|
||||
});
|
||||
|
||||
FluxDispatcher.subscribe("COLORWAYS_UPDATE_BUTTON_HEIGHT" as FluxEvents, ({ isTall }) => {
|
||||
|
@ -47,7 +36,13 @@ export default function ({
|
|||
});
|
||||
|
||||
return <Tooltip text={
|
||||
!isThin ? <><span>Colorways</span><Text variant="text-xs/normal" style={{ color: "var(--text-muted)", fontWeight: 500 }}>{"Active Colorway: " + activeColorway}</Text></> : <span>{"Active Colorway: " + activeColorway}</span>
|
||||
<>
|
||||
{!isThin ? <>
|
||||
<span>Colorways</span>
|
||||
<Text variant="text-xs/normal" style={{ color: "var(--text-muted)", fontWeight: 500 }}>{"Active Colorway: " + activeColorway}</Text>
|
||||
</> : <span>{"Active Colorway: " + activeColorway}</span>}
|
||||
{activeColorway === "Auto" ? <Text variant="text-xs/normal" style={{ color: "var(--text-muted)", fontWeight: 500 }}>{"Auto Preset: " + (getAutoPresets()[autoPreset].name || "None")}</Text> : <></>}
|
||||
</>
|
||||
} position="right" tooltipContentClassName="colorwaysBtn-tooltipContent"
|
||||
>
|
||||
{({ onMouseEnter, onMouseLeave, onClick }) => visibility ? <div className="ColorwaySelectorBtnContainer">
|
||||
|
@ -55,7 +50,8 @@ export default function ({
|
|||
className={"ColorwaySelectorBtn" + (isThin ? " ColorwaySelectorBtn_thin" : "")}
|
||||
onMouseEnter={async () => {
|
||||
onMouseEnter();
|
||||
setActiveColorway(await DataStore.get("actveColorwayID") || "None");
|
||||
setActiveColorway((await DataStore.get("activeColorwayObject") as ColorwayObject).id || "None");
|
||||
setAutoPreset(await DataStore.get("activeAutoPreset") as string);
|
||||
}}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={() => {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
|
@ -16,6 +15,7 @@ import {
|
|||
import {
|
||||
Button,
|
||||
Forms,
|
||||
Slider,
|
||||
Text,
|
||||
TextInput,
|
||||
useEffect,
|
||||
|
@ -25,12 +25,13 @@ import {
|
|||
|
||||
import { ColorPicker } from "..";
|
||||
import { knownThemeVars } from "../constants";
|
||||
import { generateCss, getPreset, gradientPresetIds, pureGradientBase } from "../css";
|
||||
import { generateCss, getPreset, gradientPresetIds, PrimarySatDiffs, pureGradientBase } from "../css";
|
||||
import { Colorway } from "../types";
|
||||
import { colorToHex, getHex, hexToString } from "../utils";
|
||||
import { colorToHex, getHex, HexToHSL, hexToString } from "../utils";
|
||||
import ColorwayCreatorSettingsModal from "./ColorwayCreatorSettingsModal";
|
||||
import ConflictingColorsModal from "./ConflictingColorsModal";
|
||||
import InputColorwayIdModal from "./InputColorwayIdModal";
|
||||
import SaveColorwayModal from "./SaveColorwayModal";
|
||||
import ThemePreviewCategory from "./ThemePreview";
|
||||
export default function ({
|
||||
modalProps,
|
||||
|
@ -48,10 +49,9 @@ export default function ({
|
|||
const [colorwayName, setColorwayName] = useState<string>("");
|
||||
const [tintedText, setTintedText] = useState<boolean>(true);
|
||||
const [discordSaturation, setDiscordSaturation] = useState<boolean>(true);
|
||||
const [collapsedSettings, setCollapsedSettings] = useState<boolean>(true);
|
||||
const [collapsedPresets, setCollapsedPresets] = useState<boolean>(true);
|
||||
const [preset, setPreset] = useState<string>("default");
|
||||
const [presetColorArray, setPresetColorArray] = useState<string[]>(["primary", "secondary", "tertiary", "accent"]);
|
||||
const [presetColorArray, setPresetColorArray] = useState<string[]>(["accent", "primary", "secondary", "tertiary"]);
|
||||
const [mutedTextBrightness, setMutedTextBrightness] = useState<number>(Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 3), 100));
|
||||
|
||||
const colorProps = {
|
||||
accent: {
|
||||
|
@ -77,11 +77,8 @@ export default function ({
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
const parsedID = colorwayID?.split("colorway:")[1];
|
||||
if (parsedID) {
|
||||
if (!parsedID) {
|
||||
throw new Error("Please enter a Colorway ID");
|
||||
} else if (!hexToString(parsedID).includes(",")) {
|
||||
if (colorwayID) {
|
||||
if (!colorwayID.includes(",")) {
|
||||
throw new Error("Invalid Colorway ID");
|
||||
} else {
|
||||
const setColor = [
|
||||
|
@ -90,7 +87,20 @@ export default function ({
|
|||
setSecondaryColor,
|
||||
setTertiaryColor
|
||||
];
|
||||
hexToString(parsedID).split(/,#/).forEach((color: string, i: number) => setColor[i](colorToHex(color)));
|
||||
colorwayID.split("|").forEach((prop: string) => {
|
||||
if (prop.includes(",#")) {
|
||||
prop.split(/,#/).forEach((color: string, i: number) => setColor[i](colorToHex(color)));
|
||||
}
|
||||
if (prop.includes("n:")) {
|
||||
setColorwayName(prop.split("n:")[1]);
|
||||
}
|
||||
if (prop.includes("p:")) {
|
||||
if (Object.values(getPreset()).map(preset => preset.id).includes(prop.split("p:")[1])) {
|
||||
setPreset(prop.split("p:")[1]);
|
||||
setPresetColorArray(getPreset()[prop.split("p:")[1]].colors);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -122,7 +132,7 @@ export default function ({
|
|||
/>
|
||||
<div className="colorwaysCreator-settingCat">
|
||||
<Forms.FormTitle style={{ marginBottom: "0" }}>
|
||||
Colors:
|
||||
Colors & Values:
|
||||
</Forms.FormTitle>
|
||||
<div className="colorwayCreator-colorPreviews">
|
||||
{presetColorArray.map(presetColor => {
|
||||
|
@ -140,6 +150,14 @@ export default function ({
|
|||
/>;
|
||||
})}
|
||||
</div>
|
||||
<Forms.FormDivider style={{ margin: "10px 0" }} />
|
||||
<Forms.FormTitle>Muted Text Brightness:</Forms.FormTitle>
|
||||
<Slider
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
initialValue={mutedTextBrightness}
|
||||
onValueChange={setMutedTextBrightness}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="colorwaysCreator-setting"
|
||||
|
@ -160,7 +178,6 @@ export default function ({
|
|||
</svg>
|
||||
</div>
|
||||
<ThemePreviewCategory
|
||||
isCollapsed={false}
|
||||
accent={"#" + accentColor}
|
||||
primary={"#" + primaryColor}
|
||||
secondary={"#" + secondaryColor}
|
||||
|
@ -170,7 +187,10 @@ export default function ({
|
|||
secondaryColor,
|
||||
tertiaryColor,
|
||||
accentColor
|
||||
)[preset].preset(discordSaturation) as { full: string, base: string; }).base})}` : ""}
|
||||
)[preset].preset(discordSaturation) as { full: string, base: string; }).base})}` : (tintedText ? `.colorwaysPreview-modal,.colorwaysPreview-wrapper {
|
||||
--primary-500: hsl(${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[500])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%) ${mutedTextBrightness || Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 3), 100)}%);
|
||||
--primary-360: hsl(${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[360])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%) 90%);
|
||||
}` : "")}
|
||||
/>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
|
@ -179,7 +199,7 @@ export default function ({
|
|||
color={Button.Colors.BRAND}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={e => {
|
||||
onClick={async () => {
|
||||
var customColorwayCSS: string = "";
|
||||
if (preset === "default") {
|
||||
customColorwayCSS = generateCss(
|
||||
|
@ -188,24 +208,26 @@ export default function ({
|
|||
tertiaryColor,
|
||||
accentColor,
|
||||
tintedText,
|
||||
discordSaturation
|
||||
discordSaturation,
|
||||
mutedTextBrightness,
|
||||
(colorwayName || "Colorway")
|
||||
);
|
||||
} else {
|
||||
gradientPresetIds.includes(getPreset()[preset].id) ?
|
||||
customColorwayCSS = getPreset(
|
||||
customColorwayCSS = (getPreset(
|
||||
primaryColor,
|
||||
secondaryColor,
|
||||
tertiaryColor,
|
||||
accentColor
|
||||
)[preset].preset(discordSaturation).full : customColorwayCSS = getPreset(
|
||||
)[preset].preset(discordSaturation) as { full: string; }).full : customColorwayCSS = (getPreset(
|
||||
primaryColor,
|
||||
secondaryColor,
|
||||
tertiaryColor,
|
||||
accentColor
|
||||
)[preset].preset(discordSaturation);
|
||||
)[preset].preset(discordSaturation) as string);
|
||||
}
|
||||
const customColorway: Colorway = {
|
||||
name: (colorwayName || "Colorway") + (preset === "default" ? "" : ": Made for " + getPreset()[preset].name),
|
||||
name: (colorwayName || "Colorway"),
|
||||
"dc-import": customColorwayCSS,
|
||||
accent: "#" + accentColor,
|
||||
primary: "#" + primaryColor,
|
||||
|
@ -215,30 +237,22 @@ export default function ({
|
|||
author: UserStore.getCurrentUser().username,
|
||||
authorID: UserStore.getCurrentUser().id,
|
||||
isGradient: gradientPresetIds.includes(getPreset()[preset].id),
|
||||
linearGradient: gradientPresetIds.includes(getPreset()[preset].id) ? getPreset(
|
||||
linearGradient: gradientPresetIds.includes(getPreset()[preset].id) ? (getPreset(
|
||||
primaryColor,
|
||||
secondaryColor,
|
||||
tertiaryColor,
|
||||
accentColor
|
||||
)[preset].preset(discordSaturation).base : null
|
||||
)[preset].preset(discordSaturation) as { base: string; }).base : "",
|
||||
preset: getPreset()[preset].id
|
||||
};
|
||||
const customColorwaysArray: Colorway[] = [customColorway];
|
||||
DataStore.get("customColorways").then(
|
||||
customColorways => {
|
||||
customColorways.forEach(
|
||||
(color: Colorway, i: number) => {
|
||||
if (color.name !== customColorway.name) {
|
||||
customColorwaysArray.push(color);
|
||||
}
|
||||
}
|
||||
);
|
||||
DataStore.set("customColorways", customColorwaysArray);
|
||||
}
|
||||
);
|
||||
modalProps.onClose();
|
||||
loadUIProps!();
|
||||
openModal(props => <SaveColorwayModal modalProps={props} colorways={[customColorway]} onFinish={() => {
|
||||
modalProps.onClose();
|
||||
loadUIProps!();
|
||||
}} />);
|
||||
}}
|
||||
>Finish</Button>
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
|
|
|
@ -41,8 +41,9 @@ export function PalleteIcon(props: IconProps) {
|
|||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M 12 7.5 C 13.242188 7.5 14.25 6.492188 14.25 5.25 C 14.25 4.007812 13.242188 3 12 3 C 10.757812 3 9.75 4.007812 9.75 5.25 C 9.75 6.492188 10.757812 7.5 12 7.5 Z M 18 12 C 19.242188 12 20.25 10.992188 20.25 9.75 C 20.25 8.507812 19.242188 7.5 18 7.5 C 16.757812 7.5 15.75 8.507812 15.75 9.75 C 15.75 10.992188 16.757812 12 18 12 Z M 8.25 10.5 C 8.25 11.742188 7.242188 12.75 6 12.75 C 4.757812 12.75 3.75 11.742188 3.75 10.5 C 3.75 9.257812 4.757812 8.25 6 8.25 C 7.242188 8.25 8.25 9.257812 8.25 10.5 Z M 9 19.5 C 10.242188 19.5 11.25 18.492188 11.25 17.25 C 11.25 16.007812 10.242188 15 9 15 C 7.757812 15 6.75 16.007812 6.75 17.25 C 6.75 18.492188 7.757812 19.5 9 19.5 Z M 9 19.5 M 24 12 C 24 16.726562 21.199219 15.878906 18.648438 15.105469 C 17.128906 14.644531 15.699219 14.210938 15 15 C 14.09375 16.023438 14.289062 17.726562 14.472656 19.378906 C 14.738281 21.742188 14.992188 24 12 24 C 5.371094 24 0 18.628906 0 12 C 0 5.371094 5.371094 0 12 0 C 18.628906 0 24 5.371094 24 12 Z M 12 22.5 C 12.917969 22.5 12.980469 22.242188 12.984375 22.234375 C 13.097656 22.015625 13.167969 21.539062 13.085938 20.558594 C 13.066406 20.304688 13.03125 20.003906 12.996094 19.671875 C 12.917969 18.976562 12.828125 18.164062 12.820312 17.476562 C 12.804688 16.417969 12.945312 15.0625 13.875 14.007812 C 14.429688 13.382812 15.140625 13.140625 15.78125 13.078125 C 16.390625 13.023438 17 13.117188 17.523438 13.234375 C 18.039062 13.351562 18.574219 13.515625 19.058594 13.660156 L 19.101562 13.675781 C 19.621094 13.832031 20.089844 13.972656 20.53125 14.074219 C 21.511719 14.296875 21.886719 14.199219 22.019531 14.109375 C 22.074219 14.070312 22.5 13.742188 22.5 12 C 22.5 6.199219 17.800781 1.5 12 1.5 C 6.199219 1.5 1.5 6.199219 1.5 12 C 1.5 17.800781 6.199219 22.5 12 22.5 Z M 12 22.5"
|
||||
/></Icon>
|
||||
d="M 12,0 C 5.3733333,0 0,5.3733333 0,12 c 0,6.626667 5.3733333,12 12,12 1.106667,0 2,-0.893333 2,-2 0,-0.52 -0.2,-0.986667 -0.52,-1.346667 -0.306667,-0.346666 -0.506667,-0.813333 -0.506667,-1.32 0,-1.106666 0.893334,-2 2,-2 h 2.36 C 21.013333,17.333333 24,14.346667 24,10.666667 24,4.7733333 18.626667,0 12,0 Z M 4.6666667,12 c -1.1066667,0 -2,-0.893333 -2,-2 0,-1.1066667 0.8933333,-2 2,-2 1.1066666,0 2,0.8933333 2,2 0,1.106667 -0.8933334,2 -2,2 z M 8.666667,6.6666667 c -1.106667,0 -2.0000003,-0.8933334 -2.0000003,-2 0,-1.1066667 0.8933333,-2 2.0000003,-2 1.106666,0 2,0.8933333 2,2 0,1.1066666 -0.893334,2 -2,2 z m 6.666666,0 c -1.106666,0 -2,-0.8933334 -2,-2 0,-1.1066667 0.893334,-2 2,-2 1.106667,0 2,0.8933333 2,2 0,1.1066666 -0.893333,2 -2,2 z m 4,5.3333333 c -1.106666,0 -2,-0.893333 -2,-2 0,-1.1066667 0.893334,-2 2,-2 1.106667,0 2,0.8933333 2,2 0,1.106667 -0.893333,2 -2,2 z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -60,3 +61,86 @@ export function CloseIcon(props: IconProps) {
|
|||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function DownloadIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "vc-download-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 2a1 1 0 0 1 1 1v10.59l3.3-3.3a1 1 0 1 1 1.4 1.42l-5 5a1 1 0 0 1-1.4 0l-5-5a1 1 0 1 1 1.4-1.42l3.3 3.3V3a1 1 0 0 1 1-1ZM3 20a1 1 0 1 0 0 2h18a1 1 0 1 0 0-2H3Z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function ImportIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "vc-import-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M.9 3a.9.9 0 0 1 .892.778l.008.123v16.201a.9.9 0 0 1-1.792.121L0 20.102V3.899A.9.9 0 0 1 .9 3Zm14.954 2.26.1-.112a1.2 1.2 0 0 1 1.584-.1l.113.1 5.998 5.998a1.2 1.2 0 0 1 .1 1.584l-.1.112-5.997 6.006a1.2 1.2 0 0 1-1.799-1.584l.1-.113 3.947-3.954H4.8a1.2 1.2 0 0 1-1.191-1.06l-.008-.14a1.2 1.2 0 0 1 1.06-1.192l.14-.008h15.103l-3.95-3.952a1.2 1.2 0 0 1-.1-1.585l.1-.112z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function IDIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "vc-id-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M15.3 14.48c-.46.45-1.08.67-1.86.67h-1.39V9.2h1.39c.78 0 1.4.22 1.86.67.46.45.68 1.22.68 2.31 0 1.1-.22 1.86-.68 2.31Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
d="M5 2a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3H5Zm1 15h2.04V7.34H6V17Zm4-9.66V17h3.44c1.46 0 2.6-.42 3.38-1.25.8-.83 1.2-2.02 1.2-3.58s-.4-2.75-1.2-3.58c-.79-.83-1.92-1.25-3.38-1.25H10Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function CodeIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "vc-code-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M9.6 7.8 4 12l5.6 4.2a1 1 0 0 1 .4.8v1.98c0 .21-.24.33-.4.2l-8.1-6.4a1 1 0 0 1 0-1.56l8.1-6.4c.16-.13.4-.01.4.2V7a1 1 0 0 1-.4.8ZM14.4 7.8 20 12l-5.6 4.2a1 1 0 0 0-.4.8v1.98c0 .21.24.33.4.2l8.1-6.4a1 1 0 0 0 0-1.56l-8.1-6.4a.25.25 0 0 0-.4.2V7a1 1 0 0 0 .4.8Z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function MoreIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "vc-more-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
d="M4 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm10-2a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm8 0a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,241 +5,258 @@
|
|||
*/
|
||||
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { CodeBlock } from "@components/CodeBlock";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { openUserProfile } from "@utils/discord";
|
||||
import {
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalProps,
|
||||
ModalRoot,
|
||||
openModal,
|
||||
} from "@utils/modal";
|
||||
import { Button, Clipboard, Forms, Text, Toasts, useState } from "@webpack/common";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Clipboard, Forms, Text, TextInput, Toasts, UserStore, useState, useStateFromStores } from "@webpack/common";
|
||||
|
||||
import { ColorwayCSS } from "..";
|
||||
import { generateCss, pureGradientBase } from "../css";
|
||||
import { Colorway } from "../types";
|
||||
import { colorToHex, stringToHex } from "../utils";
|
||||
import ThemePreviewCategory from "./ThemePreview";
|
||||
import SaveColorwayModal from "./SaveColorwayModal";
|
||||
import ThemePreview from "./ThemePreview";
|
||||
|
||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
|
||||
function RenameColorwayModal({ modalProps, ogName, onFinish, colorwayList }: { modalProps: ModalProps, ogName: string, onFinish: (name: string) => void, colorwayList: Colorway[]; }) {
|
||||
const [error, setError] = useState<string>("");
|
||||
const [newName, setNewName] = useState<string>(ogName);
|
||||
return <ModalRoot {...modalProps}>
|
||||
<ModalHeader separator={false}>
|
||||
<Text variant="heading-lg/semibold" tag="h1" style={{ marginRight: "auto" }}>
|
||||
Rename Colorway...
|
||||
</Text>
|
||||
<ModalCloseButton onClick={() => modalProps.onClose()} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<TextInput
|
||||
value={newName}
|
||||
error={error}
|
||||
onChange={setNewName}
|
||||
/>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.BRAND}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={async () => {
|
||||
if (!newName) {
|
||||
return setError("Error: Please enter a valid name");
|
||||
}
|
||||
if (colorwayList.map(c => c.name).includes(newName)) {
|
||||
return setError("Error: Name already exists");
|
||||
}
|
||||
onFinish(newName);
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => modalProps.onClose()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>;
|
||||
}
|
||||
|
||||
export default function ({
|
||||
modalProps,
|
||||
colorwayProps,
|
||||
discrimProps = false,
|
||||
colorway,
|
||||
loadUIProps
|
||||
}: {
|
||||
modalProps: ModalProps;
|
||||
colorwayProps: Colorway;
|
||||
discrimProps?: boolean;
|
||||
colorway: Colorway;
|
||||
loadUIProps: () => Promise<void>;
|
||||
}) {
|
||||
const colors: string[] = colorwayProps.colors || [
|
||||
const colors: string[] = colorway.colors || [
|
||||
"accent",
|
||||
"primary",
|
||||
"secondary",
|
||||
"tertiary",
|
||||
];
|
||||
const [collapsedCSS, setCollapsedCSS] = useState(true);
|
||||
return <ModalRoot {...modalProps} className="colorwayCreator-modal">
|
||||
<ModalHeader>
|
||||
const profile = useStateFromStores([UserStore], () => UserStore.getUser(colorway.authorID));
|
||||
return <ModalRoot {...modalProps}>
|
||||
<ModalHeader separator={false}>
|
||||
<Text variant="heading-lg/semibold" tag="h1" style={{ marginRight: "auto" }}>
|
||||
Colorway Details: {colorwayProps.name}
|
||||
Colorway: {colorway.name}
|
||||
</Text>
|
||||
<ModalCloseButton onClick={() => modalProps.onClose()} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div className="colorwayInfo-wrapper">
|
||||
<div className="colorwayInfo-colorSwatches">
|
||||
{colors.map(color => <div
|
||||
className="colorwayInfo-colorSwatch"
|
||||
style={{ backgroundColor: colorwayProps[color] }}
|
||||
<Flex style={{ gap: "8px", width: "100%" }} flexDirection="column">
|
||||
<Forms.FormTitle style={{ marginBottom: 0, width: "100%" }}>Creator:</Forms.FormTitle>
|
||||
<Flex style={{ gap: ".5rem" }}>
|
||||
<UserSummaryItem
|
||||
users={[profile]}
|
||||
guildId={undefined}
|
||||
renderIcon={false}
|
||||
showDefaultAvatarsForNullUsers
|
||||
size={32}
|
||||
showUserPopout
|
||||
/>
|
||||
<Text style={{ lineHeight: "32px" }}>{colorway.author}</Text>
|
||||
</Flex>
|
||||
<Forms.FormTitle style={{ marginBottom: 0, width: "100%" }}>Colors:</Forms.FormTitle>
|
||||
<Flex style={{ gap: "8px" }}>
|
||||
{colors.map(color => <div className="colorwayInfo-colorSwatch" style={{ backgroundColor: colorway[color] }} />)}
|
||||
</Flex>
|
||||
<Forms.FormTitle style={{ marginBottom: 0, width: "100%" }}>Actions:</Forms.FormTitle>
|
||||
<Flex style={{ gap: "8px" }} flexDirection="column">
|
||||
<Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ width: "100%" }}
|
||||
onClick={() => {
|
||||
Clipboard.copy(colorwayProps[color]);
|
||||
const colorwayIDArray = `${colorway.accent},${colorway.primary},${colorway.secondary},${colorway.tertiary}|n:${colorway.name}${colorway.preset ? `|p:${colorway.preset}` : ""}`;
|
||||
const colorwayID = stringToHex(colorwayIDArray);
|
||||
Clipboard.copy(colorwayID);
|
||||
Toasts.show({
|
||||
message: "Copied color successfully",
|
||||
message: "Copied Colorway ID Successfully",
|
||||
type: 1,
|
||||
id: "copy-colorway-color-notify",
|
||||
id: "copy-colorway-id-notify",
|
||||
});
|
||||
}}
|
||||
/>)}
|
||||
</div>
|
||||
<div className="colorwayInfo-row colorwayInfo-author">
|
||||
<Flex style={{ gap: "10px", width: "100%", alignItems: "center" }}>
|
||||
<Forms.FormTitle style={{ marginBottom: 0, width: "100%" }}>Properties:</Forms.FormTitle>
|
||||
<Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ flex: "0 0 auto", maxWidth: "236px" }}
|
||||
onClick={() => {
|
||||
openUserProfile(colorwayProps.authorID);
|
||||
}}
|
||||
>
|
||||
Author: {colorwayProps.author}
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ flex: "0 0 auto" }}
|
||||
onClick={() => {
|
||||
const colorwayIDArray = `${colorwayProps.accent},${colorwayProps.primary},${colorwayProps.secondary},${colorwayProps.tertiary}`;
|
||||
const colorwayID = stringToHex(colorwayIDArray);
|
||||
Clipboard.copy(colorwayID);
|
||||
Toasts.show({
|
||||
message: "Copied Colorway ID Successfully",
|
||||
type: 1,
|
||||
id: "copy-colorway-id-notify",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Copy Colorway ID
|
||||
</Button>
|
||||
{discrimProps && <Button
|
||||
color={Button.Colors.RED}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
style={{ flex: "0 0 auto" }}
|
||||
onClick={async () => {
|
||||
const customColorways = await DataStore.get("customColorways");
|
||||
const actveColorwayID = await DataStore.get("actveColorwayID");
|
||||
const customColorwaysArray: Colorway[] = [];
|
||||
customColorways.map((color: Colorway, i: number) => {
|
||||
if (customColorways.length > 0) {
|
||||
if (color.name !== colorwayProps.name) {
|
||||
customColorwaysArray.push(color);
|
||||
}
|
||||
if (++i === customColorways.length) {
|
||||
DataStore.set("customColorways", customColorwaysArray);
|
||||
}
|
||||
if (actveColorwayID === colorwayProps.name) {
|
||||
DataStore.set("actveColorway", null);
|
||||
DataStore.set("actveColorwayID", null);
|
||||
ColorwayCSS.set("");
|
||||
}
|
||||
modalProps.onClose();
|
||||
loadUIProps();
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>}
|
||||
</Flex>
|
||||
</div>
|
||||
<div className={"colorwayInfo-row colorwayInfo-css" + (collapsedCSS ? " colorwaysCreator-settingCat-collapsed" : "")}>
|
||||
<Flex style={{ gap: "10px", width: "100%", alignItems: "center" }}>
|
||||
<Forms.FormTitle style={{ marginBottom: 0, width: "100%" }}>CSS:</Forms.FormTitle>
|
||||
<Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ flex: "0 0 auto" }}
|
||||
onClick={() => setCollapsedCSS(!collapsedCSS)}
|
||||
>
|
||||
{collapsedCSS ? "Show" : "Hide"}
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ flex: "0 0 auto" }}
|
||||
onClick={() => {
|
||||
Clipboard.copy(colorwayProps["dc-import"]);
|
||||
Toasts.show({
|
||||
message: "Copied CSS to Clipboard",
|
||||
type: 1,
|
||||
id: "copy-colorway-css-notify",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
{discrimProps ? <Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ flex: "0 0 auto" }}
|
||||
onClick={async () => {
|
||||
const customColorways = await DataStore.get("customColorways");
|
||||
const customColorwaysArray: Colorway[] = [];
|
||||
customColorways.map((color: Colorway, i: number) => {
|
||||
if (customColorways.length > 0) {
|
||||
if (color.name === colorwayProps.name) {
|
||||
color["dc-import"] = generateCss(colorToHex(color.primary) || "313338", colorToHex(color.secondary) || "2b2d31", colorToHex(color.tertiary) || "1e1f22", colorToHex(color.accent) || "5865f2", true, true);
|
||||
customColorwaysArray.push(color);
|
||||
} else {
|
||||
customColorwaysArray.push(color);
|
||||
}
|
||||
if (++i === customColorways.length) {
|
||||
DataStore.set("customColorways", customColorwaysArray);
|
||||
}
|
||||
modalProps.onClose();
|
||||
loadUIProps();
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</Button> : <Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ flex: "0 0 auto" }}
|
||||
onClick={async () => {
|
||||
const colorwaySourceFiles = await DataStore.get(
|
||||
"colorwaySourceFiles"
|
||||
);
|
||||
const responses: Response[] = await Promise.all(
|
||||
colorwaySourceFiles.map((url: string) =>
|
||||
fetch(url)
|
||||
)
|
||||
);
|
||||
const data = await Promise.all(
|
||||
responses.map((res: Response) =>
|
||||
res.json().then(dt => { return { colorways: dt.colorways, url: res.url }; }).catch(() => { return { colorways: [], url: res.url }; })
|
||||
));
|
||||
const colorways = data.flatMap(json => json.colorways);
|
||||
|
||||
const customColorways = await DataStore.get("customColorways");
|
||||
const customColorwaysArray: Colorway[] = [];
|
||||
colorways.map((color: Colorway, i: number) => {
|
||||
if (colorways.length > 0) {
|
||||
if (color.name === colorwayProps.name) {
|
||||
color.name += " (Custom)";
|
||||
color["dc-import"] = generateCss(colorToHex(color.primary) || "313338", colorToHex(color.secondary) || "2b2d31", colorToHex(color.tertiary) || "1e1f22", colorToHex(color.accent) || "5865f2", true, true);
|
||||
customColorwaysArray.push(color);
|
||||
}
|
||||
if (++i === colorways.length) {
|
||||
DataStore.set("customColorways", [...customColorways, ...customColorwaysArray]);
|
||||
}
|
||||
modalProps.onClose();
|
||||
loadUIProps();
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</Button>}
|
||||
</Flex>
|
||||
<Text
|
||||
variant="code"
|
||||
selectable={true}
|
||||
className="colorwayInfo-cssCodeblock"
|
||||
>
|
||||
{colorwayProps["dc-import"]}
|
||||
</Text>
|
||||
</div>
|
||||
<ThemePreviewCategory
|
||||
isCollapsed={true}
|
||||
className="colorwayInfo-lastCat"
|
||||
accent={colorwayProps.accent}
|
||||
primary={colorwayProps.primary}
|
||||
secondary={colorwayProps.secondary}
|
||||
tertiary={colorwayProps.tertiary}
|
||||
previewCSS={colorwayProps.isGradient ? pureGradientBase + `.colorwaysPreview-modal,.colorwaysPreview-wrapper {--gradient-theme-bg: linear-gradient(${colorwayProps.linearGradient})}` : ""}
|
||||
/>
|
||||
</div>
|
||||
Copy Colorway ID
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ width: "100%" }}
|
||||
onClick={() => {
|
||||
Clipboard.copy(colorway["dc-import"]);
|
||||
Toasts.show({
|
||||
message: "Copied CSS to Clipboard",
|
||||
type: 1,
|
||||
id: "copy-colorway-css-notify",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Copy CSS
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ width: "100%" }}
|
||||
onClick={async () => {
|
||||
const newColorway = {
|
||||
...colorway,
|
||||
"dc-import": generateCss(colorToHex(colorway.primary) || "313338", colorToHex(colorway.secondary) || "2b2d31", colorToHex(colorway.tertiary) || "1e1f22", colorToHex(colorway.accent) || "5865f2", true, true, undefined, colorway.name)
|
||||
};
|
||||
openModal(props => <SaveColorwayModal modalProps={props} colorways={[newColorway]} onFinish={() => { }} />);
|
||||
}}
|
||||
>
|
||||
Update CSS
|
||||
</Button>
|
||||
{colorway.sourceType === "offline" && <Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ width: "100%" }}
|
||||
onClick={async () => {
|
||||
const offlineSources = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).map(o => o.colorways).filter(colorArr => colorArr.map(color => color.name).includes(colorway.name))[0];
|
||||
openModal(props => <RenameColorwayModal ogName={colorway.name} colorwayList={offlineSources} modalProps={props} onFinish={async (newName: string) => {
|
||||
const stores = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).map(source => {
|
||||
if (source.name === colorway.source) {
|
||||
return {
|
||||
name: source.name,
|
||||
colorways: [...source.colorways.filter(colorway => colorway.name !== colorway.name), {
|
||||
...colorway,
|
||||
name: newName
|
||||
}]
|
||||
};
|
||||
} else return source;
|
||||
});
|
||||
DataStore.set("customColorways", stores);
|
||||
if ((await DataStore.get("activeColorwayObject")).id === colorway.name) {
|
||||
DataStore.set("activeColorwayObject", { id: newName, css: colorway.name, sourceType: "offline", source: colorway.source });
|
||||
}
|
||||
modalProps.onClose();
|
||||
loadUIProps();
|
||||
}} />);
|
||||
}}
|
||||
>
|
||||
Rename
|
||||
</Button>}
|
||||
<Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ width: "100%" }}
|
||||
onClick={() => {
|
||||
openModal(props => <ModalRoot {...props} className="colorwayInfo-cssModal">
|
||||
<ModalContent><CodeBlock lang="css" content={colorway["dc-import"]} /></ModalContent>
|
||||
</ModalRoot>);
|
||||
}}
|
||||
>
|
||||
Show CSS
|
||||
</Button>
|
||||
<Button
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ width: "100%" }}
|
||||
onClick={() => {
|
||||
openModal((props: ModalProps) => <ModalRoot className="colorwaysPreview-modal" {...props}>
|
||||
<style>
|
||||
{colorway.isGradient ? pureGradientBase + `.colorwaysPreview-modal,.colorwaysPreview-wrapper {--gradient-theme-bg: linear-gradient(${colorway.linearGradient})}` : ""}
|
||||
</style>
|
||||
<ThemePreview
|
||||
accent={colorway.accent}
|
||||
primary={colorway.primary}
|
||||
secondary={colorway.secondary}
|
||||
tertiary={colorway.tertiary}
|
||||
isModal
|
||||
modalProps={props}
|
||||
/>
|
||||
</ModalRoot>);
|
||||
}}
|
||||
>
|
||||
Show preview
|
||||
</Button>
|
||||
{colorway.sourceType === "offline" && <Button
|
||||
color={Button.Colors.RED}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
style={{ width: "100%" }}
|
||||
onClick={async () => {
|
||||
const oldStores = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).filter(source => source.name !== colorway.source);
|
||||
const storeToModify = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).filter(source => source.name === colorway.source)[0];
|
||||
const newStore = { name: storeToModify.name, colorways: storeToModify.colorways.filter(colorway => colorway.name !== colorway.name) };
|
||||
DataStore.set("customColorways", [...oldStores, newStore]);
|
||||
if ((await DataStore.get("activeColorwayObject")).id === colorway.name) {
|
||||
DataStore.set("activeColorwayObject", { id: null, css: null, sourceType: null, source: null });
|
||||
ColorwayCSS.remove();
|
||||
}
|
||||
modalProps.onClose();
|
||||
loadUIProps();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>}
|
||||
</Flex>
|
||||
</Flex>
|
||||
<div style={{ width: "100%", height: "20px" }} />
|
||||
</ModalContent>
|
||||
</ModalRoot>;
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { DataStore } from "@api/index";
|
||||
import { PlusIcon } from "@components/Icons";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Button, Text, TextInput, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { Colorway } from "../types";
|
||||
import { StoreNameModal } from "./SettingsTabs/SourceManager";
|
||||
|
||||
export default function ({ modalProps, colorways, onFinish }: { modalProps: ModalProps, colorways: Colorway[], onFinish: () => void; }) {
|
||||
const [offlineColorwayStores, setOfflineColorwayStores] = useState<{ name: string, colorways: Colorway[], id?: string; }[]>([]);
|
||||
const [storename, setStorename] = useState<string>();
|
||||
const [noStoreError, setNoStoreError] = useState<boolean>(false);
|
||||
const { radioBar, item: radioBarItem, itemFilled: radioBarItemFilled, radioPositionLeft } = findByProps("radioBar");
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setOfflineColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]);
|
||||
})();
|
||||
});
|
||||
return <ModalRoot {...modalProps}>
|
||||
<ModalHeader separator={false}>
|
||||
<Text variant="heading-lg/semibold" tag="h1">Select Offline Colorway Source</Text>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
{noStoreError ? <Text variant="text-xs/normal" style={{ color: "var(--text-danger)" }}>Error: No store selected</Text> : <></>}
|
||||
{offlineColorwayStores.map(store => {
|
||||
return <div className={`${radioBarItem} ${radioBarItemFilled}`} aria-checked={storename === store.name}>
|
||||
<div
|
||||
className={`${radioBar} ${radioPositionLeft}`}
|
||||
style={{ padding: "10px" }}
|
||||
onClick={() => {
|
||||
setStorename(store.name);
|
||||
}}>
|
||||
<svg aria-hidden="true" role="img" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
||||
{storename === store.name && <circle cx="12" cy="12" r="5" className="radioIconForeground-3wH3aU" fill="currentColor" />}
|
||||
</svg>
|
||||
<Text variant="eyebrow" tag="h5">{store.name}</Text>
|
||||
</div>
|
||||
</div>;
|
||||
})}
|
||||
<div className={`${radioBarItem} ${radioBarItemFilled}`}>
|
||||
<div
|
||||
className={`${radioBar} ${radioPositionLeft}`}
|
||||
style={{ padding: "10px" }}
|
||||
onClick={() => {
|
||||
openModal(props => <StoreNameModal modalProps={props} conflicting={false} originalName="" onFinish={async e => {
|
||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: [] }]);
|
||||
setOfflineColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
||||
}} />);
|
||||
}}>
|
||||
<PlusIcon width={24} height={24} />
|
||||
<Text variant="eyebrow" tag="h5">Create new store...</Text>
|
||||
</div>
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.BRAND_NEW}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
onClick={async () => {
|
||||
setNoStoreError(false);
|
||||
if (!storename) {
|
||||
setNoStoreError(true);
|
||||
} else {
|
||||
const oldStores: { name: string, colorways: Colorway[], id?: string; }[] | undefined = await DataStore.get("customColorways");
|
||||
const storeToModify: { name: string, colorways: Colorway[], id?: string; } | undefined = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).filter(source => source.name === storename)[0];
|
||||
colorways.map((colorway, i) => {
|
||||
if (storeToModify.colorways.map(colorway => colorway.name).includes(colorway.name)) {
|
||||
openModal(props => <ModalRoot {...props}>
|
||||
<ModalHeader separator={false}>
|
||||
<Text variant="heading-lg/semibold" tag="h1">Duplicate Colorway</Text>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Text>A colorway with the same name was found in this store, what do you want to do?</Text>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.BRAND}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => {
|
||||
const newStore = { name: storeToModify.name, colorways: [...storeToModify.colorways.filter(colorwayy => colorwayy.name !== colorway.name), colorway] };
|
||||
DataStore.set("customColorways", [...oldStores!.filter(source => source.name !== storename), newStore]);
|
||||
props.onClose();
|
||||
if (i + 1 === colorways.length) {
|
||||
modalProps.onClose();
|
||||
onFinish!();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Override
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.BRAND}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => {
|
||||
function NewColorwayNameModal({ modalProps, onSelected }: { modalProps: ModalProps, onSelected: (e: string) => void; }) {
|
||||
const [errorMsg, setErrorMsg] = useState<string>();
|
||||
const [newColorwayName, setNewColorwayName] = useState("");
|
||||
return <ModalRoot {...modalProps}>
|
||||
<ModalHeader separator={false}>
|
||||
<Text variant="heading-lg/semibold" tag="h1">Select new name</Text>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<TextInput error={errorMsg} value={newColorwayName} onChange={e => setNewColorwayName(e)} placeholder="Enter valid colorway name" />
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={() => {
|
||||
setErrorMsg("");
|
||||
if (storeToModify!.colorways.map(colorway => colorway.name).includes(newColorwayName)) {
|
||||
setErrorMsg("Error: Name already exists");
|
||||
} else {
|
||||
onSelected(newColorwayName);
|
||||
if (i + 1 === colorways.length) {
|
||||
modalProps.onClose();
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={() => {
|
||||
if (i + 1 === colorways.length) {
|
||||
modalProps.onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>;
|
||||
}
|
||||
openModal(propss => <NewColorwayNameModal modalProps={propss} onSelected={e => {
|
||||
const newStore = { name: storeToModify.name, colorways: [...storeToModify.colorways, { ...colorway, name: e }] };
|
||||
DataStore.set("customColorways", [...oldStores!.filter(source => source.name !== storename), newStore]);
|
||||
props.onClose();
|
||||
if (i + 1 === colorways.length) {
|
||||
modalProps.onClose();
|
||||
onFinish!();
|
||||
}
|
||||
}} />);
|
||||
}}
|
||||
>
|
||||
Rename
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={() => {
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
Select different store
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>);
|
||||
} else {
|
||||
const newStore = { name: storeToModify.name, colorways: [...storeToModify.colorways, colorway] };
|
||||
DataStore.set("customColorways", [...oldStores!.filter(source => source.name !== storename), newStore]);
|
||||
if (i + 1 === colorways.length) {
|
||||
modalProps.onClose();
|
||||
onFinish();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={() => {
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>;
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { DataStore } from "@api/index";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { SettingsTab } from "@components/VencordSettings/shared";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { chooseFile, saveFile } from "@utils/web";
|
||||
import { Button, Card, Forms, Text } from "@webpack/common";
|
||||
|
||||
import { defaultColorwaySource } from "../../constants";
|
||||
import { generateCss } from "../../css";
|
||||
import { Colorway } from "../../types";
|
||||
|
||||
export default function () {
|
||||
return <SettingsTab title="Manage Colorways">
|
||||
<Forms.FormSection title="Import/Export">
|
||||
<Card className={classes("vc-settings-card", "vc-backup-restore-card")}>
|
||||
<Flex flexDirection="column">
|
||||
<strong>Warning</strong>
|
||||
<span>Importing a colorways file will overwrite your current custom colorways.</span>
|
||||
</Flex>
|
||||
</Card>
|
||||
<Text variant="text-md/normal" className={Margins.bottom8}>
|
||||
You can import and export your custom colorways as a JSON file.
|
||||
This allows you to easily transfer them to another device/installation.
|
||||
</Text>
|
||||
<Flex>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
onClick={async () => {
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
const [file] = await DiscordNative.fileManager.openFiles({
|
||||
filters: [
|
||||
{ name: "Discord Colorways List", extensions: ["json"] },
|
||||
{ name: "all", extensions: ["*"] }
|
||||
]
|
||||
});
|
||||
if (file) {
|
||||
try {
|
||||
await DataStore.set("customColorways", JSON.parse(new TextDecoder().decode(file.data)));
|
||||
} catch (err) {
|
||||
new Logger("DiscordColorways").error(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const file = await chooseFile("application/json");
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = async () => {
|
||||
try {
|
||||
await DataStore.set("customColorways", JSON.parse(reader.result as string));
|
||||
} catch (err) {
|
||||
new Logger("DiscordColorways").error(err);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}}>
|
||||
Import Colorways
|
||||
</Button>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
onClick={async () => {
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
DiscordNative.fileManager.saveWithDialog(JSON.stringify(await DataStore.get("customColorways") as string), "colorways.json");
|
||||
} else {
|
||||
saveFile(new File([JSON.stringify(await DataStore.get("customColorways") as string)], "colorways.json", { type: "application/json" }));
|
||||
}
|
||||
}}>
|
||||
Export Colorways
|
||||
</Button>
|
||||
</Flex>
|
||||
</Forms.FormSection>
|
||||
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
|
||||
<Forms.FormSection title="Transfer 3rd Party Colorways to local index (3rd-Party > Custom):">
|
||||
<Flex>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
onClick={async () => {
|
||||
const colorwaySourceFiles = await DataStore.get(
|
||||
"colorwaySourceFiles"
|
||||
);
|
||||
const responses: Response[] = await Promise.all(
|
||||
colorwaySourceFiles.map((url: string) =>
|
||||
fetch(url)
|
||||
)
|
||||
);
|
||||
const data = await Promise.all(
|
||||
responses.map((res: Response) =>
|
||||
res.json().then(dt => { return { colorways: dt.colorways, url: res.url }; }).catch(() => { return { colorways: [], url: res.url }; })
|
||||
));
|
||||
const thirdPartyColorwaysArr: Colorway[] = data.flatMap(json => json.url !== defaultColorwaySource ? json.colorways : []);
|
||||
const customColorways: Colorway[] = await DataStore.get("customColorways") as Colorway[];
|
||||
DataStore.set("customColorways", [...customColorways, ...thirdPartyColorwaysArr.map(({ name: nameOld, ...rest }) => ({ name: (nameOld + " (Custom)"), ...rest }))]);
|
||||
}}>
|
||||
As-Is
|
||||
</Button>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
onClick={async () => {
|
||||
const colorwaySourceFiles = await DataStore.get(
|
||||
"colorwaySourceFiles"
|
||||
);
|
||||
const responses: Response[] = await Promise.all(
|
||||
colorwaySourceFiles.map((url: string) =>
|
||||
fetch(url)
|
||||
)
|
||||
);
|
||||
const data = await Promise.all(
|
||||
responses.map((res: Response) =>
|
||||
res.json().then(dt => { return { colorways: dt.colorways, url: res.url }; }).catch(() => { return { colorways: [], url: res.url }; })
|
||||
));
|
||||
const thirdPartyColorwaysArr: Colorway[] = data.flatMap(json => json.url !== defaultColorwaySource ? json.colorways : []);
|
||||
const customColorways: Colorway[] = await DataStore.get("customColorways") as Colorway[];
|
||||
DataStore.set("customColorways", [...customColorways, ...thirdPartyColorwaysArr.map(({ name: nameOld, "dc-import": oldImport, ...rest }: Colorway) => ({ name: (nameOld + " (Custom)"), "dc-import": generateCss(rest.primary.split("#")[1] || "313338", rest.secondary.split("#")[1] || "2b2d31", rest.tertiary.split("#")[1] || "1e1f22", rest.accent.split("#")[1] || "5865f2", true, true), ...rest }))]);
|
||||
}}>
|
||||
With Updated CSS
|
||||
</Button>
|
||||
</Flex>
|
||||
</Forms.FormSection>
|
||||
</SettingsTab>;
|
||||
}
|
|
@ -12,19 +12,25 @@ export default function () {
|
|||
const [onDemand, setOnDemand] = useState<boolean>(false);
|
||||
const [onDemandTinted, setOnDemandTinted] = useState<boolean>(false);
|
||||
const [onDemandDiscordSat, setOnDemandDiscordSat] = useState<boolean>(false);
|
||||
const [onDemandOsAccent, setOnDemandOsAccent] = useState<boolean>(false);
|
||||
async function loadUI() {
|
||||
const [
|
||||
onDemandWays,
|
||||
onDemandWaysTintedText,
|
||||
onDemandWaysDiscordSaturation
|
||||
onDemandWaysDiscordSaturation,
|
||||
onDemandWaysOsAccentColor
|
||||
] = await DataStore.getMany([
|
||||
"onDemandWays",
|
||||
"onDemandWaysTintedText",
|
||||
"onDemandWaysDiscordSaturation"
|
||||
"onDemandWaysDiscordSaturation",
|
||||
"onDemandWaysOsAccentColor"
|
||||
]);
|
||||
setOnDemand(onDemandWays);
|
||||
setOnDemandTinted(onDemandWaysTintedText);
|
||||
setOnDemandDiscordSat(onDemandWaysDiscordSaturation);
|
||||
if (getComputedStyle(document.body).getPropertyValue("--os-accent-color") !== "") {
|
||||
setOnDemandOsAccent(onDemandWaysOsAccentColor);
|
||||
}
|
||||
}
|
||||
|
||||
const cached_loadUI = useCallback(loadUI, []);
|
||||
|
@ -54,7 +60,6 @@ export default function () {
|
|||
Use tinted text
|
||||
</Switch>
|
||||
<Switch
|
||||
hideBorder
|
||||
value={onDemandDiscordSat}
|
||||
onChange={(v: boolean) => {
|
||||
setOnDemandDiscordSat(v);
|
||||
|
@ -64,5 +69,16 @@ export default function () {
|
|||
>
|
||||
Use Discord's saturation
|
||||
</Switch>
|
||||
<Switch
|
||||
hideBorder
|
||||
value={onDemandOsAccent}
|
||||
onChange={(v: boolean) => {
|
||||
setOnDemandOsAccent(v);
|
||||
DataStore.set("onDemandWaysOsAccentColor", v);
|
||||
}}
|
||||
disabled={!onDemand || !getComputedStyle(document.body).getPropertyValue("--os-accent-color")}
|
||||
>
|
||||
Use Operating System's Accent Color
|
||||
</Switch>
|
||||
</SettingsTab>;
|
||||
}
|
||||
|
|
|
@ -6,192 +6,64 @@
|
|||
|
||||
import { DataStore } from "@api/index";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { CopyIcon } from "@components/Icons";
|
||||
import { Link } from "@components/Link";
|
||||
import { SettingsTab } from "@components/VencordSettings/shared";
|
||||
import { ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal";
|
||||
import {
|
||||
Button,
|
||||
Clipboard,
|
||||
FluxDispatcher,
|
||||
Forms,
|
||||
Switch,
|
||||
Text,
|
||||
TextInput,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useState
|
||||
} from "@webpack/common";
|
||||
import { FluxEvents } from "@webpack/types";
|
||||
|
||||
import { versionData } from "../../../discordColorways";
|
||||
import { defaultColorwaySource, fallbackColorways, knownColorwaySources } from "../../constants";
|
||||
import { versionData } from "../../.";
|
||||
import { fallbackColorways } from "../../constants";
|
||||
import { Colorway } from "../../types";
|
||||
import { CloseIcon } from "../Icons";
|
||||
|
||||
export default function () {
|
||||
const [colorways, setColorways] = useState<Colorway[]>([]);
|
||||
const [customColorways, setCustomColorways] = useState<Colorway[]>([]);
|
||||
const [colorwaySourceFiles, setColorwaySourceFiles] = useState<string[]>();
|
||||
const [colorsButtonVisibility, setColorsButtonVisibility] = useState<boolean>(false);
|
||||
const [isButtonThin, setIsButtonThin] = useState<boolean>(false);
|
||||
|
||||
async function loadUI() {
|
||||
const colorwaySourceFiles = await DataStore.get(
|
||||
"colorwaySourceFiles"
|
||||
);
|
||||
const responses: Response[] = await Promise.all(
|
||||
colorwaySourceFiles.map((url: string) =>
|
||||
fetch(url)
|
||||
)
|
||||
);
|
||||
const data = await Promise.all(
|
||||
responses.map((res: Response) =>
|
||||
res.json().catch(() => { return { colorways: [] }; })
|
||||
));
|
||||
const colorways = data.flatMap(json => json.colorways);
|
||||
const [
|
||||
customColorways,
|
||||
colorwaySourceFiless,
|
||||
showColorwaysButton,
|
||||
useThinMenuButton
|
||||
] = await DataStore.getMany([
|
||||
"customColorways",
|
||||
"colorwaySourceFiles",
|
||||
"showColorwaysButton",
|
||||
"useThinMenuButton"
|
||||
]);
|
||||
setColorways(colorways || fallbackColorways);
|
||||
setCustomColorways(customColorways);
|
||||
setColorwaySourceFiles(colorwaySourceFiless);
|
||||
setColorsButtonVisibility(showColorwaysButton);
|
||||
setIsButtonThin(useThinMenuButton);
|
||||
}
|
||||
|
||||
const cached_loadUI = useCallback(loadUI, []);
|
||||
const [showLabelsInSelectorGridView, setShowLabelsInSelectorGridView] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
cached_loadUI();
|
||||
(async function () {
|
||||
const [
|
||||
customColorways,
|
||||
colorwaySourceFiles,
|
||||
showColorwaysButton,
|
||||
useThinMenuButton,
|
||||
showLabelsInSelectorGridView
|
||||
] = await DataStore.getMany([
|
||||
"customColorways",
|
||||
"colorwaySourceFiles",
|
||||
"showColorwaysButton",
|
||||
"useThinMenuButton",
|
||||
"showLabelsInSelectorGridView"
|
||||
]);
|
||||
const responses: Response[] = await Promise.all(
|
||||
colorwaySourceFiles.map((url: string) =>
|
||||
fetch(url)
|
||||
)
|
||||
);
|
||||
const data = await Promise.all(
|
||||
responses.map((res: Response) =>
|
||||
res.json().catch(() => { return { colorways: [] }; })
|
||||
));
|
||||
const colorways = data.flatMap(json => json.colorways);
|
||||
setColorways(colorways || fallbackColorways);
|
||||
setCustomColorways(customColorways.map(source => source.colorways).flat(2));
|
||||
setColorsButtonVisibility(showColorwaysButton);
|
||||
setIsButtonThin(useThinMenuButton);
|
||||
setShowLabelsInSelectorGridView(showLabelsInSelectorGridView);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return <SettingsTab title="Settings">
|
||||
<div className="colorwaysSettingsPage-wrapper">
|
||||
<Flex style={{ gap: "0", marginBottom: "8px" }}>
|
||||
<Forms.FormTitle tag="h5" style={{ width: "100%", marginBottom: "0", lineHeight: "32px" }}>Sources</Forms.FormTitle>
|
||||
<Button
|
||||
className="colorwaysSettings-colorwaySourceAction"
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
style={{ flexShrink: "0" }}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
onClick={() => {
|
||||
openModal(props => {
|
||||
var colorwaySource = "";
|
||||
return <ModalRoot {...props} className="colorwaySourceModal">
|
||||
<ModalHeader>
|
||||
<Text variant="heading-lg/semibold" tag="h1">
|
||||
Add a source:
|
||||
</Text>
|
||||
</ModalHeader>
|
||||
<TextInput
|
||||
placeholder="Enter a valid URL..."
|
||||
onChange={e => colorwaySource = e}
|
||||
style={{ margin: "8px", width: "calc(100% - 16px)" }}
|
||||
/>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.BRAND}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={async () => {
|
||||
var sourcesArr: string[] = [];
|
||||
const colorwaySourceFilesArr = await DataStore.get("colorwaySourceFiles");
|
||||
colorwaySourceFilesArr.map((source: string) => sourcesArr.push(source));
|
||||
if (colorwaySource !== defaultColorwaySource) {
|
||||
sourcesArr.push(colorwaySource);
|
||||
}
|
||||
DataStore.set("colorwaySourceFiles", sourcesArr);
|
||||
setColorwaySourceFiles(sourcesArr);
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => props.onClose()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>;
|
||||
});
|
||||
}}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"
|
||||
/>
|
||||
</svg>
|
||||
Add a source...
|
||||
</Button>
|
||||
</Flex>
|
||||
<Flex flexDirection="column">
|
||||
{colorwaySourceFiles?.map((colorwaySourceFile: string) => <div className="colorwaysSettings-colorwaySource">
|
||||
{knownColorwaySources.find(o => o.url === colorwaySourceFile) ? <div className="hoverRoll">
|
||||
<Text className="colorwaysSettings-colorwaySourceLabel hoverRoll_normal">
|
||||
{knownColorwaySources.find(o => o.url === colorwaySourceFile)!.name} {colorwaySourceFile === defaultColorwaySource && <div className="colorways-badge">DEFAULT</div>}
|
||||
</Text>
|
||||
<Text className="colorwaysSettings-colorwaySourceLabel hoverRoll_hovered">
|
||||
{colorwaySourceFile}
|
||||
</Text>
|
||||
</div>
|
||||
: <Text className="colorwaysSettings-colorwaySourceLabel">
|
||||
{colorwaySourceFile}
|
||||
</Text>}
|
||||
{colorwaySourceFile !== defaultColorwaySource
|
||||
&& <Button
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
size={Button.Sizes.ICON}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={async () => {
|
||||
var sourcesArr: string[] = [];
|
||||
const colorwaySourceFilesArr = await DataStore.get("colorwaySourceFiles");
|
||||
colorwaySourceFilesArr.map((source: string) => {
|
||||
if (source !== colorwaySourceFile) {
|
||||
sourcesArr.push(source);
|
||||
}
|
||||
});
|
||||
DataStore.set("colorwaySourceFiles", sourcesArr);
|
||||
setColorwaySourceFiles(sourcesArr);
|
||||
}}
|
||||
>
|
||||
<CloseIcon width={20} height={20} />
|
||||
</Button>}
|
||||
<Button
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
size={Button.Sizes.ICON}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={() => { Clipboard.copy(colorwaySourceFile); }}
|
||||
>
|
||||
<CopyIcon width={20} height={20} />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Flex>
|
||||
<Forms.FormDivider style={{ margin: "20px 0" }} />
|
||||
<Forms.FormTitle tag="h5">Quick Switch</Forms.FormTitle>
|
||||
<Switch
|
||||
value={colorsButtonVisibility}
|
||||
|
@ -221,6 +93,16 @@ export default function () {
|
|||
>
|
||||
Use thin Quick Switch button
|
||||
</Switch>
|
||||
<Forms.FormTitle tag="h5">Selector</Forms.FormTitle>
|
||||
<Switch
|
||||
value={showLabelsInSelectorGridView}
|
||||
onChange={(v: boolean) => {
|
||||
setShowLabelsInSelectorGridView(v);
|
||||
DataStore.set("showLabelsInSelectorGridView", v);
|
||||
}}
|
||||
>
|
||||
Show labels in Grid View
|
||||
</Switch>
|
||||
<Flex flexDirection="column" style={{ gap: 0 }}>
|
||||
<h1 style={{
|
||||
fontFamily: "var(--font-headline)",
|
||||
|
@ -272,8 +154,7 @@ export default function () {
|
|||
marginBottom: "8px"
|
||||
}}
|
||||
>
|
||||
{versionData.creatorVersion}{" "}
|
||||
(Stable)
|
||||
{versionData.creatorVersion}{" (Stable)"}
|
||||
</Text>
|
||||
<Forms.FormTitle style={{ marginBottom: 0 }}>
|
||||
Loaded Colorways:
|
||||
|
@ -287,7 +168,7 @@ export default function () {
|
|||
marginBottom: "8px"
|
||||
}}
|
||||
>
|
||||
{[...colorways, ...customColorways].length}
|
||||
{[...colorways, ...customColorways].length + 1}
|
||||
</Text>
|
||||
<Forms.FormTitle style={{ marginBottom: 0 }}>
|
||||
Project Repositories:
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { DataStore } from "@api/index";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { CopyIcon, DeleteIcon } from "@components/Icons";
|
||||
import { SettingsTab } from "@components/VencordSettings/shared";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||
import { chooseFile, saveFile } from "@utils/web";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Button, Clipboard, Forms, ScrollerThin, Text, TextInput, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { defaultColorwaySource } from "../../constants";
|
||||
import { Colorway } from "../../types";
|
||||
import { DownloadIcon, ImportIcon } from "../Icons";
|
||||
import Spinner from "../Spinner";
|
||||
|
||||
export function StoreNameModal({ modalProps, originalName, onFinish, conflicting }: { modalProps: ModalProps, originalName: string, onFinish: (newName: string) => Promise<void>, conflicting: boolean; }) {
|
||||
const [error, setError] = useState<string>("");
|
||||
const [newStoreName, setNewStoreName] = useState<string>(originalName);
|
||||
return <ModalRoot {...modalProps}>
|
||||
<ModalHeader separator={false}>
|
||||
<Text variant="heading-lg/semibold" tag="h1">{conflicting ? "Duplicate Store Name" : "Give this store a name"}</Text>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
{conflicting ? <Text>A store with the same name already exists. Please give a different name to the imported store:</Text> : <></>}
|
||||
<Forms.FormTitle>Name:</Forms.FormTitle>
|
||||
<TextInput error={error} value={newStoreName} onChange={e => setNewStoreName(e)} style={{ marginBottom: "16px" }} />
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.BRAND}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={async () => {
|
||||
setError("");
|
||||
if ((await DataStore.get("customColorways")).map(store => store.name).includes(newStoreName)) {
|
||||
return setError("Error: Store name already exists");
|
||||
}
|
||||
onFinish(newStoreName);
|
||||
modalProps.onClose();
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={() => modalProps.onClose()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>;
|
||||
}
|
||||
|
||||
function AddOnlineStoreModal({ modalProps, onFinish }: { modalProps: ModalProps, onFinish: (name: string, url: string) => void; }) {
|
||||
const [colorwaySourceName, setColorwaySourceName] = useState<string>("");
|
||||
const [colorwaySourceURL, setColorwaySourceURL] = useState<string>("");
|
||||
const [nameError, setNameError] = useState<string>("");
|
||||
const [URLError, setURLError] = useState<string>("");
|
||||
return <ModalRoot {...modalProps}>
|
||||
<ModalHeader separator={false}>
|
||||
<Text variant="heading-lg/semibold" tag="h1">
|
||||
Add a source:
|
||||
</Text>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<Forms.FormTitle>Name:</Forms.FormTitle>
|
||||
<TextInput
|
||||
placeholder="Enter a valid Name..."
|
||||
onChange={setColorwaySourceName}
|
||||
value={colorwaySourceName}
|
||||
error={nameError}
|
||||
/>
|
||||
<Forms.FormTitle style={{ marginTop: "8px" }}>URL:</Forms.FormTitle>
|
||||
<TextInput
|
||||
placeholder="Enter a valid URL..."
|
||||
onChange={setColorwaySourceURL}
|
||||
value={colorwaySourceURL}
|
||||
error={URLError}
|
||||
style={{ marginBottom: "16px" }}
|
||||
/>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.BRAND}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={async () => {
|
||||
const sourcesArr: { name: string, url: string; }[] = (await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]);
|
||||
if (!colorwaySourceName) {
|
||||
setNameError("Error: Please enter a valid name");
|
||||
}
|
||||
else if (!colorwaySourceURL) {
|
||||
setURLError("Error: Please enter a valid URL");
|
||||
}
|
||||
else if (sourcesArr.map(s => s.name).includes(colorwaySourceName)) {
|
||||
setNameError("Error: An online source with that name already exists");
|
||||
}
|
||||
else if (sourcesArr.map(s => s.url).includes(colorwaySourceURL)) {
|
||||
setURLError("Error: An online source with that url already exists");
|
||||
} else {
|
||||
onFinish(colorwaySourceName, colorwaySourceURL);
|
||||
modalProps.onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: 8 }}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onClick={() => modalProps.onClose()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalRoot>;
|
||||
}
|
||||
|
||||
export default function () {
|
||||
const [colorwaySourceFiles, setColorwaySourceFiles] = useState<{ name: string, url: string; }[]>();
|
||||
const [customColorwayStores, setCustomColorwayStores] = useState<{ name: string, colorways: Colorway[]; }[]>([]);
|
||||
|
||||
const { item: radioBarItem, itemFilled: radioBarItemFilled } = findByProps("radioBar");
|
||||
|
||||
useEffect(() => {
|
||||
(async function () {
|
||||
setColorwaySourceFiles(await DataStore.get("colorwaySourceFiles"));
|
||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
||||
})();
|
||||
}, []);
|
||||
return <SettingsTab title="Sources">
|
||||
<Flex style={{ gap: "0", marginBottom: "8px", alignItems: "center" }}>
|
||||
<Forms.FormTitle tag="h5" style={{ marginBottom: 0, flexGrow: 1 }}>Online</Forms.FormTitle>
|
||||
<Button
|
||||
className="colorwaysSettings-colorwaySourceAction"
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
style={{ flexShrink: "0" }}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
onClick={() => {
|
||||
openModal(props => <AddOnlineStoreModal modalProps={props} onFinish={async (name, url) => {
|
||||
await DataStore.set("colorwaySourceFiles", [...await DataStore.get("colorwaySourceFiles"), { name: name, url: url }]);
|
||||
setColorwaySourceFiles([...await DataStore.get("colorwaySourceFiles"), { name: name, url: url }]);
|
||||
}} />);
|
||||
}}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"
|
||||
/>
|
||||
</svg>
|
||||
Add...
|
||||
</Button>
|
||||
</Flex>
|
||||
<ScrollerThin orientation="vertical" style={{ maxHeight: "250px" }} className="colorwaysSettings-sourceScroller">
|
||||
{colorwaySourceFiles?.map((colorwaySourceFile: { name: string, url: string; }, i: number) => <div className={`${radioBarItem} ${radioBarItemFilled} colorwaysSettings-colorwaySource`}>
|
||||
<div className="hoverRoll">
|
||||
<Text className="colorwaysSettings-colorwaySourceLabel hoverRoll_normal">
|
||||
{colorwaySourceFile.name} {colorwaySourceFile.url === defaultColorwaySource && <div className="colorways-badge">Built-In</div>}
|
||||
</Text>
|
||||
<Text className="colorwaysSettings-colorwaySourceLabel hoverRoll_hovered">
|
||||
{colorwaySourceFile.url}
|
||||
</Text>
|
||||
</div>
|
||||
<Button
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
size={Button.Sizes.ICON}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={() => { Clipboard.copy(colorwaySourceFile.url); }}
|
||||
>
|
||||
<CopyIcon width={20} height={20} />
|
||||
</Button>
|
||||
{colorwaySourceFile.url !== defaultColorwaySource
|
||||
&& <>
|
||||
<Button
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
size={Button.Sizes.ICON}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={async () => {
|
||||
openModal(props => <StoreNameModal conflicting={false} modalProps={props} originalName={colorwaySourceFile.name || ""} onFinish={async e => {
|
||||
const modal = openModal(propss => <ModalRoot {...propss} className="colorwaysLoadingModal"><Spinner style={{ color: "#ffffff" }} /></ModalRoot>);
|
||||
const res = await fetch(colorwaySourceFile.url);
|
||||
const data = await res.json();
|
||||
DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: data.colorways || [] }]);
|
||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
||||
closeModal(modal);
|
||||
}} />);
|
||||
}}
|
||||
>
|
||||
<DownloadIcon width={20} height={20} />
|
||||
</Button>
|
||||
<Button
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
size={Button.Sizes.ICON}
|
||||
color={Button.Colors.RED}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={async () => {
|
||||
DataStore.set("colorwaySourceFiles", (await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter((src, ii) => ii !== i));
|
||||
setColorwaySourceFiles((await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter((src, ii) => ii !== i));
|
||||
}}
|
||||
>
|
||||
<DeleteIcon width={20} height={20} />
|
||||
</Button>
|
||||
</>}
|
||||
</div>
|
||||
)}
|
||||
</ScrollerThin>
|
||||
<Flex style={{ gap: "0", marginBottom: "8px", alignItems: "center" }}>
|
||||
<Forms.FormTitle tag="h5" style={{ marginBottom: 0, flexGrow: 1 }}>Offline</Forms.FormTitle>
|
||||
<Button
|
||||
className="colorwaysSettings-colorwaySourceAction"
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
style={{ flexShrink: "0", marginLeft: "8px" }}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
onClick={async () => {
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
const [file] = await DiscordNative.fileManager.openFiles({
|
||||
filters: [
|
||||
{ name: "DiscordColorways Offline Store", extensions: ["json"] },
|
||||
{ name: "all", extensions: ["*"] }
|
||||
]
|
||||
});
|
||||
if (file) {
|
||||
try {
|
||||
if ((await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]).map(store => store.name).includes(JSON.parse(new TextDecoder().decode(file.data)).name)) {
|
||||
openModal(props => <StoreNameModal conflicting modalProps={props} originalName={JSON.parse(new TextDecoder().decode(file.data)).name} onFinish={async e => {
|
||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: JSON.parse(new TextDecoder().decode(file.data)).colorways }]);
|
||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
||||
}} />);
|
||||
} else {
|
||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), JSON.parse(new TextDecoder().decode(file.data))]);
|
||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
||||
}
|
||||
} catch (err) {
|
||||
new Logger("DiscordColorways").error(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const file = await chooseFile("application/json");
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = async () => {
|
||||
try {
|
||||
if ((await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]).map(store => store.name).includes(JSON.parse(reader.result as string).name)) {
|
||||
openModal(props => <StoreNameModal conflicting modalProps={props} originalName={JSON.parse(reader.result as string).name} onFinish={async e => {
|
||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: JSON.parse(reader.result as string).colorways }]);
|
||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
||||
}} />);
|
||||
} else {
|
||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), JSON.parse(reader.result as string)]);
|
||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
||||
}
|
||||
} catch (err) {
|
||||
new Logger("DiscordColorways").error(err);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ImportIcon width={14} height={14} />
|
||||
Import...
|
||||
</Button>
|
||||
<Button
|
||||
className="colorwaysSettings-colorwaySourceAction"
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
style={{ flexShrink: "0", marginLeft: "8px" }}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
onClick={() => {
|
||||
openModal(props => <StoreNameModal conflicting={false} modalProps={props} originalName="" onFinish={async e => {
|
||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: [] }]);
|
||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
||||
props.onClose();
|
||||
}} />);
|
||||
}}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"
|
||||
/>
|
||||
</svg>
|
||||
New...
|
||||
</Button>
|
||||
</Flex>
|
||||
<Flex flexDirection="column" style={{ gap: 0 }}>
|
||||
{getComputedStyle(document.body).getPropertyValue("--os-accent-color") ? <div className={`${radioBarItem} ${radioBarItemFilled} colorwaysSettings-colorwaySource`}>
|
||||
<Flex style={{ gap: 0, alignItems: "center", width: "100%", height: "30px" }}>
|
||||
<Text className="colorwaysSettings-colorwaySourceLabel">OS Accent Color{" "}
|
||||
<div className="colorways-badge">Built-In</div>
|
||||
</Text>
|
||||
</Flex>
|
||||
</div> : <></>}
|
||||
{customColorwayStores.map(({ name: customColorwaySourceName, colorways: offlineStoreColorways }) => <div className={`${radioBarItem} ${radioBarItemFilled} colorwaysSettings-colorwaySource`}>
|
||||
|
||||
<Text className="colorwaysSettings-colorwaySourceLabel">
|
||||
{customColorwaySourceName}
|
||||
</Text>
|
||||
<Button
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
size={Button.Sizes.ICON}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={async () => {
|
||||
console.log(offlineStoreColorways);
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
DiscordNative.fileManager.saveWithDialog(JSON.stringify({ "name": customColorwaySourceName, "colorways": [...offlineStoreColorways] }), `${customColorwaySourceName.replaceAll(" ", "-").toLowerCase()}.colorways.json`);
|
||||
} else {
|
||||
saveFile(new File([JSON.stringify({ "name": customColorwaySourceName, "colorways": [...offlineStoreColorways] })], `${customColorwaySourceName.replaceAll(" ", "-").toLowerCase()}.colorways.json`, { type: "application/json" }));
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DownloadIcon width={20} height={20} />
|
||||
</Button>
|
||||
<Button
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
size={Button.Sizes.ICON}
|
||||
color={Button.Colors.RED}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={async () => {
|
||||
var sourcesArr: { name: string, colorways: Colorway[]; }[] = [];
|
||||
const customColorwaySources = await DataStore.get("customColorways");
|
||||
customColorwaySources.map((source: { name: string, colorways: Colorway[]; }) => {
|
||||
if (source.name !== customColorwaySourceName) {
|
||||
sourcesArr.push(source);
|
||||
}
|
||||
});
|
||||
DataStore.set("customColorways", sourcesArr);
|
||||
setCustomColorwayStores(sourcesArr);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon width={20} height={20} />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Flex>
|
||||
</SettingsTab>;
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { DataStore } from "@api/index";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { DeleteIcon } from "@components/Icons";
|
||||
import { Link } from "@components/Link";
|
||||
import { SettingsTab } from "@components/VencordSettings/shared";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Button, ScrollerThin, Text, TextInput, Tooltip, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { StoreItem } from "../../types";
|
||||
import { DownloadIcon } from "../Icons";
|
||||
|
||||
export default function () {
|
||||
const [storeObject, setStoreObject] = useState<StoreItem[]>([]);
|
||||
const [colorwaySourceFiles, setColorwaySourceFiles] = useState<{ name: string, url: string; }[]>([]);
|
||||
const [searchValue, setSearchValue] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
if (!searchValue) {
|
||||
(async function () {
|
||||
const res: Response = await fetch("https://dablulite.vercel.app/");
|
||||
const data = await res.json();
|
||||
setStoreObject(data.sources);
|
||||
setColorwaySourceFiles(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]);
|
||||
})();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const { item: radioBarItem, itemFilled: radioBarItemFilled } = findByProps("radioBar");
|
||||
|
||||
return <SettingsTab title="Colorway Store">
|
||||
<Flex style={{ gap: "0", marginBottom: "8px" }}>
|
||||
<TextInput
|
||||
className="colorwaySelector-search"
|
||||
placeholder="Search for sources..."
|
||||
value={searchValue}
|
||||
onChange={setSearchValue}
|
||||
/>
|
||||
<Tooltip text="Refresh...">
|
||||
{({ onMouseEnter, onMouseLeave }) => <Button
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
size={Button.Sizes.ICON}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.OUTLINED}
|
||||
style={{ marginLeft: "8px" }}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={async function () {
|
||||
const res: Response = await fetch("https://dablulite.vercel.app/");
|
||||
const data = await res.json();
|
||||
setStoreObject(data.sources);
|
||||
setColorwaySourceFiles(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="20"
|
||||
height="20"
|
||||
style={{ padding: "6px", boxSizing: "content-box" }}
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<rect
|
||||
y="0"
|
||||
fill="none"
|
||||
width="24"
|
||||
height="24"
|
||||
/>
|
||||
<path
|
||||
d="M6.351,6.351C7.824,4.871,9.828,4,12,4c4.411,0,8,3.589,8,8h2c0-5.515-4.486-10-10-10 C9.285,2,6.779,3.089,4.938,4.938L3,3v6h6L6.351,6.351z"
|
||||
/>
|
||||
<path
|
||||
d="M17.649,17.649C16.176,19.129,14.173,20,12,20c-4.411,0-8-3.589-8-8H2c0,5.515,4.486,10,10,10 c2.716,0,5.221-1.089,7.062-2.938L21,21v-6h-6L17.649,17.649z"
|
||||
/>
|
||||
</svg>
|
||||
</Button>}
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
<ScrollerThin orientation="vertical" className="colorwaysSettings-sourceScroller">
|
||||
{storeObject.map((item: StoreItem) =>
|
||||
item.name.toLowerCase().includes(searchValue.toLowerCase()) ? <div className={`${radioBarItem} ${radioBarItemFilled} colorwaysSettings-colorwaySource`}>
|
||||
<Flex flexDirection="column" style={{ gap: ".5rem" }}>
|
||||
<Text className="colorwaysSettings-colorwaySourceLabelHeader">
|
||||
{item.name}
|
||||
</Text>
|
||||
<Text className="colorwaysSettings-colorwaySourceDesc">
|
||||
{item.description}
|
||||
</Text>
|
||||
<Link className="colorwaysSettings-colorwaySourceDesc" href={"https://github.com/" + item.authorGh}>by {item.authorGh}</Link>
|
||||
</Flex>
|
||||
<Button
|
||||
innerClassName="colorwaysSettings-iconButtonInner"
|
||||
size={Button.Sizes.ICON}
|
||||
color={colorwaySourceFiles.map(source => source.name).includes(item.name) ? Button.Colors.RED : Button.Colors.PRIMARY}
|
||||
look={Button.Looks.OUTLINED}
|
||||
onClick={async () => {
|
||||
if (colorwaySourceFiles.map(source => source.name).includes(item.name)) {
|
||||
const sourcesArr: { name: string, url: string; }[] = colorwaySourceFiles.filter(source => source.name !== item.name);
|
||||
DataStore.set("colorwaySourceFiles", sourcesArr);
|
||||
setColorwaySourceFiles(sourcesArr);
|
||||
} else {
|
||||
const sourcesArr: { name: string, url: string; }[] = [...colorwaySourceFiles, { name: item.name, url: item.url }];
|
||||
DataStore.set("colorwaySourceFiles", sourcesArr);
|
||||
setColorwaySourceFiles(sourcesArr);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{colorwaySourceFiles.map(source => source.name).includes(item.name) ? <DeleteIcon width={20} height={20} /> : <DownloadIcon width={20} height={20} />}
|
||||
</Button>
|
||||
</div> : <></>
|
||||
)}
|
||||
</ScrollerThin>
|
||||
</SettingsTab>;
|
||||
}
|
|
@ -4,8 +4,10 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export default function ({ className }: { className?: string; }) {
|
||||
return <div className={"colorwaysBtn-spinner" + (className ? " " + className : "")} role="img" aria-label="Loading">
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export default function ({ className, style }: { className?: string, style?: CSSProperties; }) {
|
||||
return <div className={"colorwaysBtn-spinner" + (className ? ` ${className}` : "")} role="img" aria-label="Loading" style={style}>
|
||||
<div className="colorwaysBtn-spinnerInner">
|
||||
<svg className="colorwaysBtn-spinnerCircular" viewBox="25 25 50 50" fill="currentColor">
|
||||
<circle className="colorwaysBtn-spinnerBeam colorwaysBtn-spinnerBeam3" cx="50" cy="50" r="20" />
|
||||
|
|
|
@ -5,188 +5,146 @@
|
|||
*/
|
||||
|
||||
import { ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||
import {
|
||||
Forms,
|
||||
Text
|
||||
} from "@webpack/common";
|
||||
import { Text } from "@webpack/common";
|
||||
|
||||
import { HexToHSL } from "../utils";
|
||||
import { CloseIcon } from "./Icons";
|
||||
|
||||
export default function ({
|
||||
export default function ThemePreview({
|
||||
accent,
|
||||
primary,
|
||||
secondary,
|
||||
tertiary,
|
||||
className,
|
||||
isCollapsed,
|
||||
previewCSS,
|
||||
noContainer
|
||||
modalProps,
|
||||
isModal
|
||||
}: {
|
||||
accent: string,
|
||||
primary: string,
|
||||
secondary: string,
|
||||
tertiary: string,
|
||||
className?: string,
|
||||
isCollapsed: boolean,
|
||||
previewCSS?: string,
|
||||
noContainer?: boolean;
|
||||
modalProps?: ModalProps,
|
||||
isModal?: boolean;
|
||||
}) {
|
||||
function ThemePreview({
|
||||
accent,
|
||||
primary,
|
||||
secondary,
|
||||
tertiary,
|
||||
isModal,
|
||||
modalProps
|
||||
}: {
|
||||
accent: string,
|
||||
primary: string,
|
||||
secondary: string,
|
||||
tertiary: string,
|
||||
isModal?: boolean,
|
||||
modalProps?: ModalProps;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className="colorwaysPreview-wrapper"
|
||||
style={{ background: `var(--dc-overlay-app-frame, ${tertiary})` }}
|
||||
>
|
||||
<div className="colorwaysPreview-titlebar" />
|
||||
<div className="colorwaysPreview-body">
|
||||
<div className="colorwayPreview-guilds">
|
||||
<div className="colorwayPreview-guild">
|
||||
<div
|
||||
className="colorwayPreview-guildItem"
|
||||
style={{ background: `var(--dc-guild-button, ${primary})` }}
|
||||
onMouseEnter={e => e.currentTarget.style.background = accent}
|
||||
onMouseLeave={e => e.currentTarget.style.background = `var(--dc-guild-button, ${primary})`}
|
||||
onClick={() => {
|
||||
if (isModal) {
|
||||
modalProps?.onClose();
|
||||
} else {
|
||||
openModal((props: ModalProps) => <ModalRoot className="colorwaysPreview-modal" {...props}>
|
||||
<style>
|
||||
{previewCSS}
|
||||
</style>
|
||||
<ThemePreview accent={accent} primary={primary} secondary={secondary} tertiary={tertiary} isModal modalProps={props} />
|
||||
</ModalRoot>);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isModal ? <CloseIcon style={{ color: "var(--header-secondary)" }} /> : <svg
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19,3H14V5h5v5h2V5A2,2,0,0,0,19,3Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19,19H14v2h5a2,2,0,0,0,2-2V14H19Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M3,5v5H5V5h5V3H5A2,2,0,0,0,3,5Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5,14H3v5a2,2,0,0,0,2,2h5V19H5Z"
|
||||
/>
|
||||
</svg>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="colorwayPreview-guild">
|
||||
<div className="colorwayPreview-guildSeparator" style={{ backgroundColor: primary }} />
|
||||
</div>
|
||||
<div className="colorwayPreview-guild">
|
||||
<div
|
||||
className="colorwayPreview-guildItem"
|
||||
style={{ background: `var(--dc-guild-button, ${primary})` }}
|
||||
onMouseEnter={e => e.currentTarget.style.background = accent}
|
||||
onMouseLeave={e => e.currentTarget.style.background = `var(--dc-guild-button, ${primary})`}
|
||||
/>
|
||||
</div>
|
||||
<div className="colorwayPreview-guild">
|
||||
<div
|
||||
className="colorwayPreview-guildItem"
|
||||
style={{ background: `var(--dc-guild-button, ${primary})` }}
|
||||
onMouseEnter={e => e.currentTarget.style.background = accent}
|
||||
onMouseLeave={e => e.currentTarget.style.background = `var(--dc-guild-button, ${primary})`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="colorwayPreview-channels" style={{ background: `var(--dc-overlay-3, ${secondary})` }}>
|
||||
return <>
|
||||
<style>
|
||||
{".colorwaysPreview-wrapper {color: var(--header-secondary); box-shadow: var(--legacy-elevation-border);}" + previewCSS}
|
||||
</style>
|
||||
<div
|
||||
className="colorwaysPreview-wrapper"
|
||||
style={{ background: `var(--dc-overlay-app-frame, ${tertiary})` }}
|
||||
>
|
||||
<div className="colorwaysPreview-titlebar" />
|
||||
<div className="colorwaysPreview-body">
|
||||
<div className="colorwayPreview-guilds">
|
||||
<div className="colorwayPreview-guild">
|
||||
<div
|
||||
className="colorwayPreview-userArea"
|
||||
style={{
|
||||
background: `var(--dc-secondary-alt, hsl(${HexToHSL(secondary)[0]} ${HexToHSL(secondary)[1]}% ${Math.max(HexToHSL(secondary)[2] - 3.6, 0)}%))`
|
||||
className="colorwayPreview-guildItem"
|
||||
style={{ background: `var(--dc-guild-button, ${primary})` }}
|
||||
onMouseEnter={e => e.currentTarget.style.background = accent}
|
||||
onMouseLeave={e => e.currentTarget.style.background = `var(--dc-guild-button, ${primary})`}
|
||||
onClick={() => {
|
||||
if (isModal) {
|
||||
modalProps?.onClose();
|
||||
} else {
|
||||
openModal((props: ModalProps) => <ModalRoot className="colorwaysPreview-modal" {...props}>
|
||||
<style>
|
||||
{previewCSS}
|
||||
</style>
|
||||
<ThemePreview accent={accent} primary={primary} secondary={secondary} tertiary={tertiary} isModal modalProps={props} />
|
||||
</ModalRoot>);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="colorwayPreview-filler" />
|
||||
<div
|
||||
className="colorwayPreview-topShadow"
|
||||
style={{
|
||||
"--primary-900-hsl": `${HexToHSL(tertiary)[0]} ${HexToHSL(tertiary)[1]}% ${Math.max(HexToHSL(tertiary)[2] - (3.6 * 6), 0)}%`,
|
||||
"--primary-500-hsl": `${HexToHSL(primary)[0]} ${HexToHSL(primary)[1]}% ${Math.min(HexToHSL(primary)[2] + (3.6 * 3), 100)}%`
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<Text
|
||||
tag="div"
|
||||
variant="text-md/semibold"
|
||||
lineClamp={1}
|
||||
selectable={false}
|
||||
{isModal ? <CloseIcon style={{ color: "var(--header-secondary)" }} /> : <svg
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19,3H14V5h5v5h2V5A2,2,0,0,0,19,3Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19,19H14v2h5a2,2,0,0,0,2-2V14H19Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M3,5v5H5V5h5V3H5A2,2,0,0,0,3,5Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5,14H3v5a2,2,0,0,0,2,2h5V19H5Z"
|
||||
/>
|
||||
</svg>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="colorwayPreview-chat" style={{ background: `var(--dc-overlay-chat, ${primary})` }}>
|
||||
<div className="colorwayPreview-guild">
|
||||
<div className="colorwayPreview-guildSeparator" style={{ backgroundColor: primary }} />
|
||||
</div>
|
||||
<div className="colorwayPreview-guild">
|
||||
<div
|
||||
className="colorwayPreview-chatBox"
|
||||
style={{
|
||||
background: `var(--dc-overlay-3, hsl(${HexToHSL(primary)[0]} ${HexToHSL(primary)[1]}% ${Math.min(HexToHSL(primary)[2] + 3.6, 100)}%))`
|
||||
}}
|
||||
className="colorwayPreview-guildItem"
|
||||
style={{ background: `var(--dc-guild-button, ${primary})` }}
|
||||
onMouseEnter={e => e.currentTarget.style.background = accent}
|
||||
onMouseLeave={e => e.currentTarget.style.background = `var(--dc-guild-button, ${primary})`}
|
||||
/>
|
||||
<div className="colorwayPreview-filler" />
|
||||
</div>
|
||||
<div className="colorwayPreview-guild">
|
||||
<div
|
||||
className="colorwayPreview-topShadow"
|
||||
className="colorwayPreview-guildItem"
|
||||
style={{ background: `var(--dc-guild-button, ${primary})` }}
|
||||
onMouseEnter={e => e.currentTarget.style.background = accent}
|
||||
onMouseLeave={e => e.currentTarget.style.background = `var(--dc-guild-button, ${primary})`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="colorwayPreview-channels" style={{ background: `var(--dc-overlay-3, ${secondary})` }}>
|
||||
<div
|
||||
className="colorwayPreview-userArea"
|
||||
style={{
|
||||
background: `var(--dc-secondary-alt, hsl(${HexToHSL(secondary)[0]} ${HexToHSL(secondary)[1]}% ${Math.max(HexToHSL(secondary)[2] - 3.6, 0)}%))`
|
||||
}}
|
||||
/>
|
||||
<div className="colorwayPreview-filler">
|
||||
<div className="colorwayPreview-channel" style={{ backgroundColor: "var(--white-500)" }} />
|
||||
<div className="colorwayPreview-channel" style={{ backgroundColor: "var(--primary-360)" }} />
|
||||
<div className="colorwayPreview-channel" style={{ backgroundColor: "var(--primary-500)" }} />
|
||||
</div>
|
||||
<div
|
||||
className="colorwayPreview-topShadow"
|
||||
style={{
|
||||
"--primary-900-hsl": `${HexToHSL(tertiary)[0]} ${HexToHSL(tertiary)[1]}% ${Math.max(HexToHSL(tertiary)[2] - (3.6 * 6), 0)}%`,
|
||||
"--primary-500-hsl": `${HexToHSL(primary)[0]} ${HexToHSL(primary)[1]}% ${Math.min(HexToHSL(primary)[2] + (3.6 * 3), 100)}%`
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<Text
|
||||
tag="div"
|
||||
variant="text-md/semibold"
|
||||
lineClamp={1}
|
||||
selectable={false}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className="colorwayPreview-chat" style={{ background: `var(--dc-overlay-chat, ${primary})` }}>
|
||||
<div
|
||||
className="colorwayPreview-chatBox"
|
||||
style={{
|
||||
background: `var(--dc-overlay-3, hsl(${HexToHSL(primary)[0]} ${HexToHSL(primary)[1]}% ${Math.min(HexToHSL(primary)[2] + 3.6, 100)}%))`
|
||||
}}
|
||||
/>
|
||||
<div className="colorwayPreview-filler" />
|
||||
<div
|
||||
className="colorwayPreview-topShadow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
!noContainer ? <div className="colorwaysPreview">
|
||||
<Forms.FormTitle
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
Preview
|
||||
</Forms.FormTitle>
|
||||
<style>
|
||||
{previewCSS}
|
||||
</style>
|
||||
<ThemePreview
|
||||
accent={accent}
|
||||
primary={primary}
|
||||
secondary={secondary}
|
||||
tertiary={tertiary}
|
||||
/>
|
||||
</div> : <>
|
||||
<style>
|
||||
{".colorwaysPreview-wrapper {color: var(--header-secondary); box-shadow: var(--legacy-elevation-border);}" + previewCSS}
|
||||
</style>
|
||||
<ThemePreview
|
||||
accent={accent}
|
||||
primary={primary}
|
||||
secondary={secondary}
|
||||
tertiary={tertiary}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
|
|
|
@ -6,17 +6,6 @@
|
|||
|
||||
export const defaultColorwaySource = "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json";
|
||||
|
||||
export const knownColorwaySources = [
|
||||
{
|
||||
name: "Project Colorway",
|
||||
url: "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json"
|
||||
},
|
||||
{
|
||||
name: "DaBluLite's Personal Colorways",
|
||||
url: "https://raw.githubusercontent.com/DaBluLite/dablulite.github.io/master/colorways/index.json"
|
||||
}
|
||||
];
|
||||
|
||||
export const fallbackColorways = [
|
||||
{
|
||||
name: "Keyboard Purple",
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { UserStore } from "@webpack/common";
|
||||
import { Plugins } from "Vencord";
|
||||
|
||||
import { HexToHSL } from "./utils";
|
||||
|
@ -213,7 +214,7 @@ export const colorVariables: string[] = [
|
|||
"green-100",
|
||||
];
|
||||
|
||||
const PrimarySatDiffs = {
|
||||
export const PrimarySatDiffs = {
|
||||
130: 63.9594,
|
||||
160: 49.4382,
|
||||
200: 37.5758,
|
||||
|
@ -234,7 +235,7 @@ const PrimarySatDiffs = {
|
|||
800: 25,
|
||||
};
|
||||
|
||||
const BrandSatDiffs = {
|
||||
export const BrandSatDiffs = {
|
||||
100: -9.54712,
|
||||
130: 2.19526,
|
||||
160: -1.17509,
|
||||
|
@ -262,7 +263,7 @@ const BrandSatDiffs = {
|
|||
900: -52.5074
|
||||
};
|
||||
|
||||
const BrandLightDiffs = {
|
||||
export const BrandLightDiffs = {
|
||||
100: 33.5,
|
||||
130: 32.2,
|
||||
160: 30.2,
|
||||
|
@ -469,8 +470,14 @@ export function gradientBase(accentColor?: string, discordSaturation = false) {
|
|||
}`;
|
||||
}
|
||||
|
||||
export function generateCss(primaryColor: string, secondaryColor: string, tertiaryColor: string, accentColor: string, tintedText: boolean, discordSaturation: boolean) {
|
||||
const colorwayCss = `/*Automatically Generated - Colorway Creator V${(Plugins.plugins.DiscordColorways as any).creatorVersion}*/
|
||||
export function generateCss(primaryColor: string, secondaryColor: string, tertiaryColor: string, accentColor: string, tintedText: boolean, discordSaturation: boolean, mutedTextBrightness?: number, name?: string) {
|
||||
return `/**
|
||||
* @name ${name}
|
||||
* @version ${(Plugins.plugins.DiscordColorways as any).creatorVersion}
|
||||
* @description Automatically generated Colorway.
|
||||
* @author ${UserStore.getCurrentUser().username}
|
||||
* @authorId ${UserStore.getCurrentUser().id}
|
||||
*/
|
||||
:root:root {
|
||||
--brand-100-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[100])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[100]) * 10) / 10, 0)};
|
||||
--brand-130-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[130])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[130]) * 10) / 10, 0)}%;
|
||||
|
@ -498,6 +505,8 @@ export function generateCss(primaryColor: string, secondaryColor: string, tertia
|
|||
--brand-830-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[830])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[830]) * 10) / 10, 100)}%;
|
||||
--brand-860-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[860])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[860]) * 10) / 10, 100)}%;
|
||||
--brand-900-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[900])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[900]) * 10) / 10, 100)}%;
|
||||
}
|
||||
.theme-dark {
|
||||
--primary-800-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + tertiaryColor)[1] / 100) * (100 + PrimarySatDiffs[800])) * 10) / 10 : HexToHSL("#" + tertiaryColor)[1]}%) ${Math.max(HexToHSL("#" + tertiaryColor)[2] - (3.6 * 2), 0)}%;
|
||||
--primary-730-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + tertiaryColor)[1] / 100) * (100 + PrimarySatDiffs[730])) * 10) / 10 : HexToHSL("#" + tertiaryColor)[1]}%) ${Math.max(HexToHSL("#" + tertiaryColor)[2] - 3.6, 0)}%;
|
||||
--primary-700-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + tertiaryColor)[1]}%) ${HexToHSL("#" + tertiaryColor)[2]}%;
|
||||
|
@ -507,18 +516,29 @@ export function generateCss(primaryColor: string, secondaryColor: string, tertia
|
|||
--primary-600-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${HexToHSL("#" + primaryColor)[2]}%;
|
||||
--primary-560-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + 3.6, 100)}%;
|
||||
--primary-530-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[530])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 2), 100)}%;
|
||||
--primary-500-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[500])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 3), 100)}%;
|
||||
--interactive-muted: hsl(${HexToHSL("#" + primaryColor)[0]} 0% ${Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 3), 100)}%);
|
||||
--primary-500-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[500])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%) ${mutedTextBrightness || Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 3), 100)}%;
|
||||
--interactive-muted: hsl(${HexToHSL("#" + primaryColor)[0]} ${HexToHSL("#" + primaryColor)[1] / 2}% ${Math.max(Math.min(HexToHSL("#" + primaryColor)[2] - 5, 100), 45)}%);
|
||||
${tintedText ? `--primary-460-hsl: 0 calc(var(--saturation-factor, 1)*0%) 50%;
|
||||
--primary-430: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[430])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%), 90%)` : "hsl(" + HexToHSL("#" + secondaryColor)[0] + ", calc(var(--saturation-factor, 1)*100%), 20%)")};
|
||||
--primary-400: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[400])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%), 90%)` : "hsl(" + HexToHSL("#" + secondaryColor)[0] + ", calc(var(--saturation-factor, 1)*100%), 20%)")};
|
||||
--primary-360: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[360])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%), 90%)` : "hsl(" + HexToHSL("#" + secondaryColor)[0] + ", calc(var(--saturation-factor, 1)*100%), 20%)")};` : ""}
|
||||
--primary-430: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL(`#${primaryColor}`)[1] / 100) * (100 + PrimarySatDiffs[430])) * 10) / 10 : HexToHSL(`#${primaryColor}`)[1]}%), 90%)` : `hsl(${HexToHSL(`#${secondaryColor}`)[0]}, calc(var(--saturation-factor, 1)*100%), 20%)`)};
|
||||
--primary-400: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL(`#${primaryColor}`)[1] / 100) * (100 + PrimarySatDiffs[400])) * 10) / 10 : HexToHSL(`#${primaryColor}`)[1]}%), 90%)` : `hsl(${HexToHSL(`#${secondaryColor}`)[0]}, calc(var(--saturation-factor, 1)*100%), 20%)`)};
|
||||
--primary-360: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL(`#${primaryColor}`)[1] / 100) * (100 + PrimarySatDiffs[360])) * 10) / 10 : HexToHSL(`#${primaryColor}`)[1]}%), 90%)` : `hsl(${HexToHSL(`#${secondaryColor}`)[0]}, calc(var(--saturation-factor, 1)*100%), 20%)`)};` : ""}
|
||||
}
|
||||
.theme-light {
|
||||
--white-500-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + 80, 90)}%;
|
||||
--primary-130-hsl: ${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + secondaryColor)[1]}%) ${Math.min(HexToHSL("#" + secondaryColor)[2] + 80, 85)}%;
|
||||
--primary-160-hsl: ${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + secondaryColor)[1] / 100) * (100 + PrimarySatDiffs[660])) * 10) / 10 : HexToHSL("#" + secondaryColor)[1]}%) ${Math.min(HexToHSL("#" + secondaryColor)[2] + 76.4, 82.5)}%;
|
||||
--primary-200-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + tertiaryColor)[1]}%) ${Math.min(HexToHSL("#" + tertiaryColor)[2] + 80, 80)}%;
|
||||
}
|
||||
.emptyPage_feb902,
|
||||
.scrollerContainer_dda72c,
|
||||
.container__03ec9,
|
||||
.header__71942 {
|
||||
background-color: unset !important;
|
||||
}
|
||||
.container__6b2e5,
|
||||
.container__03ec9,
|
||||
.header__71942 {
|
||||
background: transparent !important;
|
||||
}${(Math.round(HexToHSL("#" + primaryColor)[2]) > 80) ? `\n\n/*Primary*/
|
||||
.theme-dark .container_bd15da,
|
||||
.theme-dark .body__616e6,
|
||||
|
@ -545,12 +565,6 @@ export function generateCss(primaryColor: string, secondaryColor: string, tertia
|
|||
--white-500: black !important;
|
||||
}
|
||||
|
||||
.theme-dark .container__6b2e5,
|
||||
.theme-dark .container__03ec9,
|
||||
.theme-dark .header__71942 {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.theme-dark .container__26baa {
|
||||
--channel-icon: black;
|
||||
}
|
||||
|
@ -657,26 +671,216 @@ export function generateCss(primaryColor: string, secondaryColor: string, tertia
|
|||
--mention-foreground: black !important;
|
||||
}
|
||||
/*End Accent*/`: ""}`;
|
||||
return colorwayCss;
|
||||
}
|
||||
|
||||
export function getPreset(primaryColor?: string, secondaryColor?: string, tertiaryColor?: string, accentColor?: string) {
|
||||
function cyan(discordSaturation = false) {
|
||||
export function getAutoPresets(accentColor?: string) {
|
||||
function hueRotation() {
|
||||
return `:root:root {
|
||||
--cyan-accent-color: ${"#" + accentColor};
|
||||
--brand-100-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[100]) * 10) / 10, 0)};
|
||||
--brand-130-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[130]) * 10) / 10, 0)}%;
|
||||
--brand-160-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[160]) * 10) / 10, 0)}%;
|
||||
--brand-200-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[200]) * 10) / 10, 0)}%;
|
||||
--brand-230-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[230]) * 10) / 10, 0)}%;
|
||||
--brand-260-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[260]) * 10) / 10, 0)}%;
|
||||
--brand-300-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[300]) * 10) / 10, 0)}%;
|
||||
--brand-330-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[330]) * 10) / 10, 0)}%;
|
||||
--brand-345-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[345]) * 10) / 10, 0)}%;
|
||||
--brand-360-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[360]) * 10) / 10, 0)}%;
|
||||
--brand-400-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[400]) * 10) / 10, 0)}%;
|
||||
--brand-430-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[430]) * 10) / 10, 0)}%;
|
||||
--brand-460-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[460]) * 10) / 10, 0)}%;
|
||||
--brand-500-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${HexToHSL("#" + accentColor)[2]}%;
|
||||
--brand-530-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[530]) * 10) / 10, 100)}%;
|
||||
--brand-560-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[560]) * 10) / 10, 100)}%;
|
||||
--brand-600-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[600]) * 10) / 10, 100)}%;
|
||||
--brand-630-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[630]) * 10) / 10, 100)}%;
|
||||
--brand-660-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[660]) * 10) / 10, 100)}%;
|
||||
--brand-700-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[700]) * 10) / 10, 100)}%;
|
||||
--brand-730-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[730]) * 10) / 10, 100)}%;
|
||||
--brand-760-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[760]) * 10) / 10, 100)}%;
|
||||
--brand-800-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[800]) * 10) / 10, 100)}%;
|
||||
--brand-830-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[830]) * 10) / 10, 100)}%;
|
||||
--brand-860-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[860]) * 10) / 10, 100)}%;
|
||||
--brand-900-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[900]) * 10) / 10, 100)}%;
|
||||
--primary-800-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*12%) 7%;
|
||||
--primary-730-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*10%) 13%;
|
||||
--primary-700-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*10%) 13%;
|
||||
--primary-660-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 15%;
|
||||
--primary-645-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 16%;
|
||||
--primary-630-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 18%;
|
||||
--primary-600-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 21%;
|
||||
--primary-560-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 24%;
|
||||
--primary-530-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 24%;
|
||||
--primary-500-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 24%;
|
||||
}`;
|
||||
}
|
||||
|
||||
function accentSwap() {
|
||||
return `:root:root {
|
||||
--brand-100-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[100]) * 10) / 10, 0)};
|
||||
--brand-130-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[130]) * 10) / 10, 0)}%;
|
||||
--brand-160-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[160]) * 10) / 10, 0)}%;
|
||||
--brand-200-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[200]) * 10) / 10, 0)}%;
|
||||
--brand-230-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[230]) * 10) / 10, 0)}%;
|
||||
--brand-260-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[260]) * 10) / 10, 0)}%;
|
||||
--brand-300-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[300]) * 10) / 10, 0)}%;
|
||||
--brand-330-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[330]) * 10) / 10, 0)}%;
|
||||
--brand-345-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[345]) * 10) / 10, 0)}%;
|
||||
--brand-360-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[360]) * 10) / 10, 0)}%;
|
||||
--brand-400-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[400]) * 10) / 10, 0)}%;
|
||||
--brand-430-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[430]) * 10) / 10, 0)}%;
|
||||
--brand-460-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[460]) * 10) / 10, 0)}%;
|
||||
--brand-500-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${HexToHSL("#" + accentColor)[2]}%;
|
||||
--brand-530-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[530]) * 10) / 10, 100)}%;
|
||||
--brand-560-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[560]) * 10) / 10, 100)}%;
|
||||
--brand-600-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[600]) * 10) / 10, 100)}%;
|
||||
--brand-630-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[630]) * 10) / 10, 100)}%;
|
||||
--brand-660-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[660]) * 10) / 10, 100)}%;
|
||||
--brand-700-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[700]) * 10) / 10, 100)}%;
|
||||
--brand-730-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[730]) * 10) / 10, 100)}%;
|
||||
--brand-760-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[760]) * 10) / 10, 100)}%;
|
||||
--brand-800-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[800]) * 10) / 10, 100)}%;
|
||||
--brand-830-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[830]) * 10) / 10, 100)}%;
|
||||
--brand-860-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[860]) * 10) / 10, 100)}%;
|
||||
--brand-900-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[900]) * 10) / 10, 100)}%;
|
||||
}`;
|
||||
}
|
||||
|
||||
function materialYou() {
|
||||
return `:root:root {
|
||||
--brand-100-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*90.5%) 56.5;
|
||||
--brand-130-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*102.2%) 55.2%;
|
||||
--brand-160-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*98.8%) 53.2%;
|
||||
--brand-200-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*97.3%) 51.2%;
|
||||
--brand-230-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*101.6%) 49.3%;
|
||||
--brand-260-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*100.7%) 46.9%;
|
||||
--brand-300-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*100.6%) 44.2%;
|
||||
--brand-330-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*99.4%) 39.9%;
|
||||
--brand-345-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*99.5%) 37.1%;
|
||||
--brand-360-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*100.6%) 35.8%;
|
||||
--brand-400-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*100.6%) 30.1%;
|
||||
--brand-430-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*100.1%) 28.1%;
|
||||
--brand-460-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*99.9%) 25.8%;
|
||||
--brand-500-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*100%) 23%;
|
||||
--brand-530-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*75.2%) 17.1%;
|
||||
--brand-560-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*50.1%) 10.7%;
|
||||
--brand-600-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*41.2%) 2.4%;
|
||||
--brand-630-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*41.2%) -3.5%;
|
||||
--brand-660-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*42%) -8.4%;
|
||||
--brand-700-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*41.8%) -15.8%;
|
||||
--brand-730-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*41.4%) -17.4%;
|
||||
--brand-760-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*41.6%) -19.5%;
|
||||
--brand-800-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*42.7%) -22.3%;
|
||||
--brand-830-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*42.6%) -26.8%;
|
||||
--brand-860-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*41.6%) -32.1%;
|
||||
--brand-900-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*47.5%) -38.6%;
|
||||
--primary-800-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*10%) 10.8%;
|
||||
--primary-730-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*8%) 14.4%;
|
||||
--primary-700-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*16%) 18%;
|
||||
--primary-660-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*14%) 12.4%;
|
||||
--primary-645-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*14%) 14.9%;
|
||||
--primary-630-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*12%) 16%;
|
||||
--primary-600-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*12%) 12%;
|
||||
--primary-560-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*12%) 15.6%;
|
||||
--primary-530-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*12%) 19.2%;
|
||||
--primary-500-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*12%) 22.8%;
|
||||
--primary-460-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*12%) 50%;
|
||||
--primary-430: hsl(${HexToHSL("#" + accentColor)[0]}, calc(var(--saturation-factor, 1)*12%), 90%);
|
||||
--primary-400: hsl(${HexToHSL("#" + accentColor)[0]}, calc(var(--saturation-factor, 1)*12%), 90%);
|
||||
--primary-360: hsl(${HexToHSL("#" + accentColor)[0]}, calc(var(--saturation-factor, 1)*12%), 90%);
|
||||
}
|
||||
.emptyPage_feb902,
|
||||
.scrollerContainer_dda72c,
|
||||
.container__03ec9,
|
||||
.header__71942 {
|
||||
background-color: unset !important;
|
||||
}`;
|
||||
}
|
||||
|
||||
return {
|
||||
hueRotation: {
|
||||
name: "Hue Rotation",
|
||||
id: "hueRotation",
|
||||
preset: hueRotation
|
||||
},
|
||||
accentSwap: {
|
||||
name: "Accent Swap",
|
||||
id: "accentSwap",
|
||||
preset: accentSwap
|
||||
},
|
||||
materialYou: {
|
||||
name: "Material You",
|
||||
id: "materialYou",
|
||||
preset: materialYou
|
||||
}
|
||||
} as { [key: string]: { name: string, id: string, preset: () => string; }; };
|
||||
}
|
||||
|
||||
export function getPreset(primaryColor?: string, secondaryColor?: string, tertiaryColor?: string, accentColor?: string): { [preset: string]: { name: string, preset: (...args: any) => string | { full: string, base: string; }, id: string, colors: string[]; }; } {
|
||||
function cyanLegacy(discordSaturation = false) {
|
||||
return `:root:root {
|
||||
--cyan-accent-color: #${accentColor};
|
||||
--cyan-background-primary: hsl(${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${HexToHSL("#" + primaryColor)[2]}%/40%);
|
||||
--cyan-background-secondary: hsl(${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + tertiaryColor)[1]}%) ${Math.min(HexToHSL("#" + tertiaryColor)[2] + (3.6 * 2), 100)}%);
|
||||
}`;
|
||||
}
|
||||
|
||||
function cyan2(discordSaturation = false) {
|
||||
function cyan(discordSaturation = false) {
|
||||
return `:root:root {
|
||||
--cyan-accent-color: ${"#" + accentColor};
|
||||
--cyan-accent-color: #${accentColor};
|
||||
--cyan-background-primary: hsl(${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${HexToHSL("#" + primaryColor)[2]}%/60%);
|
||||
--cyan-second-layer: hsl(${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + tertiaryColor)[1]}%) ${Math.min(HexToHSL("#" + tertiaryColor)[2] + (3.6 * 2), 100)}%/60%);
|
||||
}`;
|
||||
}
|
||||
|
||||
function nexusRemastered(discordSaturation = false) {
|
||||
return `:root:root {
|
||||
--nexus-accent-color: #${accentColor};
|
||||
--nexus-background-secondary: hsl(${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + tertiaryColor)[1] / 100) * (100 + PrimarySatDiffs[800])) * 10) / 10 : HexToHSL("#" + tertiaryColor)[1]}%) ${Math.max(HexToHSL("#" + tertiaryColor)[2] - (3.6 * 2), 0)}%);
|
||||
--nexus-background-elevated: hsl(${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + tertiaryColor)[1] / 100) * (100 + PrimarySatDiffs[800])) * 10) / 10 : HexToHSL("#" + tertiaryColor)[1]}%) ${Math.max(HexToHSL("#" + tertiaryColor)[2] - (3.6 * 2), 0)}%);
|
||||
--nexus-background-floating: hsl(${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + tertiaryColor)[1] / 100) * (100 + PrimarySatDiffs[800])) * 10) / 10 : HexToHSL("#" + tertiaryColor)[1]}%) ${Math.max(HexToHSL("#" + tertiaryColor)[2] - (3.6 * 2), 0)}%);
|
||||
--nexus-background-tertiary: hsl(${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + tertiaryColor)[1]}%) ${HexToHSL("#" + tertiaryColor)[2]}%);
|
||||
--home-background: hsl(${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + tertiaryColor)[1]}%) ${HexToHSL("#" + tertiaryColor)[2]}%);
|
||||
--nexus-background-primary: hsl(${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${HexToHSL("#" + primaryColor)[2]}%);
|
||||
--primary-800-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + tertiaryColor)[1] / 100) * (100 + PrimarySatDiffs[800])) * 10) / 10 : HexToHSL("#" + tertiaryColor)[1]}%) ${Math.max(HexToHSL("#" + tertiaryColor)[2] - (3.6 * 2), 0)}%;
|
||||
--primary-730-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + tertiaryColor)[1] / 100) * (100 + PrimarySatDiffs[730])) * 10) / 10 : HexToHSL("#" + tertiaryColor)[1]}%) ${Math.max(HexToHSL("#" + tertiaryColor)[2] - 3.6, 0)}%;
|
||||
--primary-700-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + tertiaryColor)[1]}%) ${HexToHSL("#" + tertiaryColor)[2]}%;
|
||||
--primary-660-hsl: ${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + secondaryColor)[1] / 100) * (100 + PrimarySatDiffs[660])) * 10) / 10 : HexToHSL("#" + secondaryColor)[1]}%) ${Math.max(HexToHSL("#" + secondaryColor)[2] - 3.6, 0)}%;
|
||||
--primary-645-hsl: ${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + secondaryColor)[1] / 100) * (100 + PrimarySatDiffs[645])) * 10) / 10 : HexToHSL("#" + secondaryColor)[1]}%) ${Math.max(HexToHSL("#" + secondaryColor)[2] - 1.1, 0)}%;
|
||||
--primary-630-hsl: ${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + secondaryColor)[1]}%) ${HexToHSL("#" + secondaryColor)[2]}%;
|
||||
--primary-600-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${HexToHSL("#" + primaryColor)[2]}%;
|
||||
--primary-560-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + 3.6, 100)}%;
|
||||
--primary-530-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[530])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 2), 100)}%;
|
||||
--primary-500-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[500])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 3), 100)}%;
|
||||
--primary-200: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[200])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%), 90%)` : "hsl(" + HexToHSL("#" + secondaryColor)[0] + ", calc(var(--saturation-factor, 1)*100%), 20%)")}
|
||||
}
|
||||
.theme-dark {
|
||||
--background-tertiary: var(--primary-700) !important;
|
||||
}
|
||||
.theme-light {
|
||||
--background-tertiary: var(--primary-200) !important;
|
||||
}`;
|
||||
}
|
||||
|
||||
function modular(discordSaturation = false) {
|
||||
return `:root:root {
|
||||
--brand-experiment: #${accentColor};
|
||||
--primary-800-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + tertiaryColor)[1] / 100) * (100 + PrimarySatDiffs[800])) * 10) / 10 : HexToHSL("#" + tertiaryColor)[1]}%) ${Math.max(HexToHSL("#" + tertiaryColor)[2] - (3.6 * 2), 0)}%;
|
||||
--primary-730-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + tertiaryColor)[1] / 100) * (100 + PrimarySatDiffs[730])) * 10) / 10 : HexToHSL("#" + tertiaryColor)[1]}%) ${Math.max(HexToHSL("#" + tertiaryColor)[2] - 3.6, 0)}%;
|
||||
--primary-700-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + tertiaryColor)[1]}%) ${HexToHSL("#" + tertiaryColor)[2]}%;
|
||||
--primary-660-hsl: ${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + secondaryColor)[1] / 100) * (100 + PrimarySatDiffs[660])) * 10) / 10 : HexToHSL("#" + secondaryColor)[1]}%) ${Math.max(HexToHSL("#" + secondaryColor)[2] - 3.6, 0)}%;
|
||||
--primary-645-hsl: ${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + secondaryColor)[1] / 100) * (100 + PrimarySatDiffs[645])) * 10) / 10 : HexToHSL("#" + secondaryColor)[1]}%) ${Math.max(HexToHSL("#" + secondaryColor)[2] - 1.1, 0)}%;
|
||||
--primary-630-hsl: ${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + secondaryColor)[1]}%) ${HexToHSL("#" + secondaryColor)[2]}%;
|
||||
--primary-600-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${HexToHSL("#" + primaryColor)[2]}%;
|
||||
--primary-560-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + 3.6, 100)}%;
|
||||
--primary-530-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[530])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 2), 100)}% !important;
|
||||
--primary-500-hsl: ${HexToHSL("#" + primaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[500])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%) ${Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 3), 100)}% !important;
|
||||
--primary-330: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[330])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%), 90%)` : "hsl(" + HexToHSL("#" + secondaryColor)[0] + ", calc(var(--saturation-factor, 1)*100%), 20%)")};
|
||||
--primary-360: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[360])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%), 90%)` : "hsl(" + HexToHSL("#" + secondaryColor)[0] + ", calc(var(--saturation-factor, 1)*100%), 20%)")};
|
||||
--primary-400: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + `, calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + primaryColor)[1] / 100) * (100 + PrimarySatDiffs[400])) * 10) / 10 : HexToHSL("#" + primaryColor)[1]}%), 90%)` : "hsl(" + HexToHSL("#" + secondaryColor)[0] + ", calc(var(--saturation-factor, 1)*100%), 20%)")}
|
||||
}`;
|
||||
}
|
||||
|
||||
function virtualBoy(discordSaturation = false) {
|
||||
return `:root:root {
|
||||
--VBaccent: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${HexToHSL("#" + accentColor)[2]}%;
|
||||
|
@ -685,14 +889,6 @@ export function getPreset(primaryColor?: string, secondaryColor?: string, tertia
|
|||
}`;
|
||||
}
|
||||
|
||||
function modular(discordSaturation = false) {
|
||||
return `:root:root {
|
||||
--modular-hue: ${HexToHSL("#" + accentColor)[0]};
|
||||
--modular-saturation: calc(var(--saturation-factor, 1)${HexToHSL("#" + accentColor)[1]}%);
|
||||
--modular-lightness: ${HexToHSL("#" + accentColor)[2]}%;
|
||||
}`;
|
||||
}
|
||||
|
||||
function solana(discordSaturation = false) {
|
||||
return `:root:root {
|
||||
--accent-hue: ${HexToHSL("#" + accentColor)[0]};
|
||||
|
@ -724,78 +920,6 @@ export function getPreset(primaryColor?: string, secondaryColor?: string, tertia
|
|||
};
|
||||
}
|
||||
|
||||
function hueRotation(discordSaturation = false) {
|
||||
return `:root:root {
|
||||
--brand-100-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[100])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[100]) * 10) / 10, 0)};
|
||||
--brand-130-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[130])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[130]) * 10) / 10, 0)}%;
|
||||
--brand-160-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[160])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[160]) * 10) / 10, 0)}%;
|
||||
--brand-200-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[200])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[200]) * 10) / 10, 0)}%;
|
||||
--brand-230-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[230])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[230]) * 10) / 10, 0)}%;
|
||||
--brand-260-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[260])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[260]) * 10) / 10, 0)}%;
|
||||
--brand-300-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[300])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[300]) * 10) / 10, 0)}%;
|
||||
--brand-330-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[330])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[330]) * 10) / 10, 0)}%;
|
||||
--brand-345-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[345])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[345]) * 10) / 10, 0)}%;
|
||||
--brand-360-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[360])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[360]) * 10) / 10, 0)}%;
|
||||
--brand-400-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[400])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[400]) * 10) / 10, 0)}%;
|
||||
--brand-430-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[430])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[430]) * 10) / 10, 0)}%;
|
||||
--brand-460-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[460])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[460]) * 10) / 10, 0)}%;
|
||||
--brand-500-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${HexToHSL("#" + accentColor)[2]}%;
|
||||
--brand-530-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[530])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[530]) * 10) / 10, 100)}%;
|
||||
--brand-560-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[560])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[560]) * 10) / 10, 100)}%;
|
||||
--brand-600-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[600])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[600]) * 10) / 10, 100)}%;
|
||||
--brand-630-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[630])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[630]) * 10) / 10, 100)}%;
|
||||
--brand-660-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[660])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[660]) * 10) / 10, 100)}%;
|
||||
--brand-700-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[700])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[700]) * 10) / 10, 100)}%;
|
||||
--brand-730-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[730])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[730]) * 10) / 10, 100)}%;
|
||||
--brand-760-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[760])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[760]) * 10) / 10, 100)}%;
|
||||
--brand-800-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[800])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[800]) * 10) / 10, 100)}%;
|
||||
--brand-830-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[830])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[830]) * 10) / 10, 100)}%;
|
||||
--brand-860-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[860])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[860]) * 10) / 10, 100)}%;
|
||||
--brand-900-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[900])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[900]) * 10) / 10, 100)}%;
|
||||
--primary-800-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*12%) 7%;
|
||||
--primary-730-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*10%) 13%;
|
||||
--primary-700-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*10%) 13%;
|
||||
--primary-660-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 15%;
|
||||
--primary-645-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 16%;
|
||||
--primary-630-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 18%;
|
||||
--primary-600-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 21%;
|
||||
--primary-560-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 24%;
|
||||
--primary-530-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 24%;
|
||||
--primary-500-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*11%) 24%;
|
||||
}`;
|
||||
}
|
||||
|
||||
function accentSwap(discordSaturation = false) {
|
||||
return `:root:root {
|
||||
--brand-100-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[100])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[100]) * 10) / 10, 0)};
|
||||
--brand-130-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[130])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[130]) * 10) / 10, 0)}%;
|
||||
--brand-160-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[160])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[160]) * 10) / 10, 0)}%;
|
||||
--brand-200-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[200])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[200]) * 10) / 10, 0)}%;
|
||||
--brand-230-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[230])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[230]) * 10) / 10, 0)}%;
|
||||
--brand-260-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[260])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[260]) * 10) / 10, 0)}%;
|
||||
--brand-300-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[300])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[300]) * 10) / 10, 0)}%;
|
||||
--brand-330-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[330])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[330]) * 10) / 10, 0)}%;
|
||||
--brand-345-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[345])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[345]) * 10) / 10, 0)}%;
|
||||
--brand-360-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[360])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[360]) * 10) / 10, 0)}%;
|
||||
--brand-400-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[400])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[400]) * 10) / 10, 0)}%;
|
||||
--brand-430-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[430])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[430]) * 10) / 10, 0)}%;
|
||||
--brand-460-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[460])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.max(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[460]) * 10) / 10, 0)}%;
|
||||
--brand-500-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + accentColor)[1]}%) ${HexToHSL("#" + accentColor)[2]}%;
|
||||
--brand-530-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[530])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[530]) * 10) / 10, 100)}%;
|
||||
--brand-560-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[560])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[560]) * 10) / 10, 100)}%;
|
||||
--brand-600-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[600])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[600]) * 10) / 10, 100)}%;
|
||||
--brand-630-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[630])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[630]) * 10) / 10, 100)}%;
|
||||
--brand-660-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[660])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[660]) * 10) / 10, 100)}%;
|
||||
--brand-700-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[700])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[700]) * 10) / 10, 100)}%;
|
||||
--brand-730-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[730])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[730]) * 10) / 10, 100)}%;
|
||||
--brand-760-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[760])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[760]) * 10) / 10, 100)}%;
|
||||
--brand-800-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[800])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[800]) * 10) / 10, 100)}%;
|
||||
--brand-830-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[830])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[830]) * 10) / 10, 100)}%;
|
||||
--brand-860-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[860])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[860]) * 10) / 10, 100)}%;
|
||||
--brand-900-hsl: ${HexToHSL("#" + accentColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + accentColor)[1] / 100) * (100 + BrandSatDiffs[900])) * 10) / 10 : HexToHSL("#" + accentColor)[1]}%) ${Math.min(Math.round((HexToHSL("#" + accentColor)[2] + BrandLightDiffs[900]) * 10) / 10, 100)}%;
|
||||
}`;
|
||||
}
|
||||
|
||||
return {
|
||||
default: {
|
||||
name: "Default",
|
||||
|
@ -809,12 +933,18 @@ export function getPreset(primaryColor?: string, secondaryColor?: string, tertia
|
|||
id: "cyan",
|
||||
colors: ["accent", "primary", "secondary"]
|
||||
},
|
||||
cyan2: {
|
||||
name: "Cyan 2",
|
||||
preset: cyan2,
|
||||
id: "cyan2",
|
||||
cyanLegacy: {
|
||||
name: "Cyan 1 (Legacy)",
|
||||
preset: cyanLegacy,
|
||||
id: "cyanLegacy",
|
||||
colors: ["accent", "primary", "secondary"]
|
||||
},
|
||||
nexusRemastered: {
|
||||
name: "Nexus Remastered",
|
||||
preset: nexusRemastered,
|
||||
id: "nexusRemastered",
|
||||
colors: ["accent", "primary", "secondary", "tertiary"]
|
||||
},
|
||||
virtualBoy: {
|
||||
name: "Virtual Boy",
|
||||
preset: virtualBoy,
|
||||
|
@ -825,7 +955,7 @@ export function getPreset(primaryColor?: string, secondaryColor?: string, tertia
|
|||
name: "Modular",
|
||||
preset: modular,
|
||||
id: "modular",
|
||||
colors: ["accent"]
|
||||
colors: ["accent", "primary", "secondary", "tertiary"]
|
||||
},
|
||||
solana: {
|
||||
name: "Solana",
|
||||
|
@ -847,19 +977,24 @@ export function getPreset(primaryColor?: string, secondaryColor?: string, tertia
|
|||
},
|
||||
hueRotation: {
|
||||
name: "Hue Rotation",
|
||||
preset: hueRotation,
|
||||
preset: getAutoPresets(accentColor).hueRotation.preset,
|
||||
id: "hueRotation",
|
||||
colors: ["accent"]
|
||||
},
|
||||
accentSwap: {
|
||||
name: "Accent Swap",
|
||||
preset: accentSwap,
|
||||
preset: getAutoPresets(accentColor).accentSwap.preset,
|
||||
id: "accentSwap",
|
||||
colors: ["accent"]
|
||||
},
|
||||
materialYou: {
|
||||
name: "Material You",
|
||||
preset: getAutoPresets(accentColor).materialYou.preset,
|
||||
id: "materialYou",
|
||||
colors: ["accent"]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const gradientPresetIds = [
|
||||
"gradientType1",
|
||||
"gradientType2"
|
||||
|
|
|
@ -8,25 +8,38 @@ import * as DataStore from "@api/DataStore";
|
|||
import { addAccessory, removeAccessory } from "@api/MessageAccessories";
|
||||
import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList";
|
||||
import { disableStyle, enableStyle } from "@api/Styles";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { ModalProps, openModal } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findByProps } from "@webpack";
|
||||
import {
|
||||
Button,
|
||||
Clipboard,
|
||||
Forms,
|
||||
Heading,
|
||||
i18n,
|
||||
SettingsRouter,
|
||||
Toasts
|
||||
} from "@webpack/common";
|
||||
import { CSSProperties } from "react";
|
||||
import { Plugins } from "Vencord";
|
||||
|
||||
import AutoColorwaySelector from "./components/AutoColorwaySelector";
|
||||
import ColorPickerModal from "./components/ColorPicker";
|
||||
import ColorwaysButton from "./components/ColorwaysButton";
|
||||
import CreatorModal from "./components/CreatorModal";
|
||||
import Selector from "./components/Selector";
|
||||
import ManageColorwaysPage from "./components/SettingsTabs/ManageColorwaysPage";
|
||||
import OnDemandWaysPage from "./components/SettingsTabs/OnDemandPage";
|
||||
import SettingsPage from "./components/SettingsTabs/SettingsPage";
|
||||
import SourceManager from "./components/SettingsTabs/SourceManager";
|
||||
import Store from "./components/SettingsTabs/Store";
|
||||
import Spinner from "./components/Spinner";
|
||||
import { defaultColorwaySource } from "./constants";
|
||||
import { generateCss, getAutoPresets } from "./css";
|
||||
import style from "./style.css?managed";
|
||||
import { ColorPickerProps } from "./types";
|
||||
import { ColorPickerProps, ColorwayObject } from "./types";
|
||||
import { colorToHex, hexToString } from "./utils";
|
||||
|
||||
export let ColorPicker: React.FunctionComponent<ColorPickerProps> = () => {
|
||||
return <Spinner className="colorways-creator-module-warning" />;
|
||||
|
@ -41,7 +54,10 @@ export let ColorPicker: React.FunctionComponent<ColorPickerProps> = () => {
|
|||
onDemandWaysTintedText,
|
||||
useThinMenuButton,
|
||||
onDemandWaysDiscordSaturation,
|
||||
onDemandWaysColorArray
|
||||
onDemandWaysOsAccentColor,
|
||||
activeColorwayObject,
|
||||
selectorViewMode,
|
||||
showLabelsInSelectorGridView
|
||||
] = await DataStore.getMany([
|
||||
"customColorways",
|
||||
"colorwaySourceFiles",
|
||||
|
@ -50,24 +66,49 @@ export let ColorPicker: React.FunctionComponent<ColorPickerProps> = () => {
|
|||
"onDemandWaysTintedText",
|
||||
"useThinMenuButton",
|
||||
"onDemandWaysDiscordSaturation",
|
||||
"onDemandWaysColorArray"
|
||||
"onDemandWaysOsAccentColor",
|
||||
"activeColorwayObject",
|
||||
"selectorViewMode",
|
||||
"showLabelsInSelectorGridView"
|
||||
]);
|
||||
|
||||
const defaults = [
|
||||
{ name: "customColorways", checkedValue: customColorways, defaults: [] },
|
||||
{ name: "colorwaySourceFiles", checkedValue: colorwaySourceFiles, defaults: [defaultColorwaySource] },
|
||||
{ name: "showColorwaysButton", checkedValue: showColorwaysButton, defaults: false },
|
||||
{ name: "onDemandWays", checkedValue: onDemandWays, defaults: false },
|
||||
{ name: "onDemandWaysTintedText", checkedValue: onDemandWaysTintedText, defaults: true },
|
||||
{ name: "useThinMenuButton", checkedValue: useThinMenuButton, defaults: false },
|
||||
{ name: "onDemandWaysDiscordSaturation", checkedValue: onDemandWaysDiscordSaturation, defaults: false },
|
||||
{ name: "onDemandWaysColorArray", checkedValue: onDemandWaysColorArray, defaults: ["313338", "2b2d31", "1e1f22", "5865f2"] }
|
||||
{ name: "showColorwaysButton", value: showColorwaysButton, default: false },
|
||||
{ name: "onDemandWays", value: onDemandWays, default: false },
|
||||
{ name: "onDemandWaysTintedText", value: onDemandWaysTintedText, default: true },
|
||||
{ name: "useThinMenuButton", value: useThinMenuButton, default: false },
|
||||
{ name: "onDemandWaysDiscordSaturation", value: onDemandWaysDiscordSaturation, default: false },
|
||||
{ name: "onDemandWaysOsAccentColor", value: onDemandWaysOsAccentColor, default: false },
|
||||
{ name: "activeColorwayObject", value: activeColorwayObject, default: { id: null, css: null, sourceType: null, source: null } },
|
||||
{ name: "selectorViewMode", value: selectorViewMode, default: "grid" },
|
||||
{ name: "showLabelsInSelectorGridView", value: showLabelsInSelectorGridView, default: false }
|
||||
];
|
||||
|
||||
defaults.forEach(({ name, checkedValue, defaults }) => {
|
||||
if (!checkedValue) DataStore.set(name, defaults);
|
||||
defaults.forEach(({ name, value, default: def }) => {
|
||||
if (!value) DataStore.set(name, def);
|
||||
});
|
||||
|
||||
if (customColorways) {
|
||||
if (!customColorways[0].colorways) {
|
||||
DataStore.set("customColorways", [{ name: "Custom", colorways: customColorways }]);
|
||||
}
|
||||
} else {
|
||||
DataStore.set("customColorways", []);
|
||||
}
|
||||
|
||||
if (colorwaySourceFiles) {
|
||||
if (typeof colorwaySourceFiles[0] === "string") {
|
||||
DataStore.set("colorwaySourceFiles", colorwaySourceFiles.map((sourceURL: string, i: number) => {
|
||||
return { name: sourceURL === defaultColorwaySource ? "Project Colorway" : `Source #${i}`, url: sourceURL };
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
DataStore.set("colorwaySourceFiles", [{
|
||||
name: "Project Colorway",
|
||||
url: defaultColorwaySource
|
||||
}]);
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
export const ColorwayCSS = {
|
||||
|
@ -85,17 +126,14 @@ export const ColorwayCSS = {
|
|||
};
|
||||
|
||||
export const versionData = {
|
||||
pluginVersion: "5.6.5.1",
|
||||
creatorVersion: "1.19",
|
||||
pluginVersion: "5.7.0b1",
|
||||
creatorVersion: "1.20",
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "DiscordColorways",
|
||||
description: "A plugin that offers easy access to simple color schemes/themes for Discord, also known as Colorways",
|
||||
authors: [{
|
||||
name: "DaBluLite",
|
||||
id: 582170007505731594n
|
||||
}, Devs.ImLvna],
|
||||
authors: [EquicordDevs.DaBluLite, Devs.ImLvna],
|
||||
dependencies: ["ServerListAPI", "MessageAccessoriesAPI"],
|
||||
pluginVersion: versionData.pluginVersion,
|
||||
creatorVersion: versionData.creatorVersion,
|
||||
|
@ -106,6 +144,22 @@ export default definePlugin({
|
|||
"Open Settings": () => SettingsRouter.open("ColorwaysSettings"),
|
||||
"Open On-Demand Settings": () => SettingsRouter.open("ColorwaysOnDemand"),
|
||||
"Manage Colorways...": () => SettingsRouter.open("ColorwaysManagement"),
|
||||
"Change Auto Colorway Preset": async () => {
|
||||
const [
|
||||
activeAutoPreset,
|
||||
activeColorwayObject
|
||||
] = await DataStore.getMany([
|
||||
"activeAutoPreset",
|
||||
"activeColorwayObject"
|
||||
]);
|
||||
openModal((props: ModalProps) => <AutoColorwaySelector autoColorwayId={activeAutoPreset} modalProps={props} onChange={autoPresetId => {
|
||||
if (activeColorwayObject.id === "Auto") {
|
||||
const demandedColorway = getAutoPresets(colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")))[autoPresetId].preset();
|
||||
DataStore.set("activeColorwayObject", { id: "Auto", css: demandedColorway, sourceType: "online", source: null });
|
||||
ColorwayCSS.set(demandedColorway);
|
||||
}
|
||||
}} />);
|
||||
}
|
||||
},
|
||||
patches: [
|
||||
// Credits to Kyuuhachi for the BetterSettings plugin patches
|
||||
|
@ -136,9 +190,23 @@ export default definePlugin({
|
|||
{
|
||||
find: "Messages.ACTIVITY_SETTINGS",
|
||||
replacement: {
|
||||
match: /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/,
|
||||
match: /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS/,
|
||||
replace: "...$self.makeSettingsCategories($1),$&"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "Messages.ACTIVITY_SETTINGS",
|
||||
replacement: {
|
||||
match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/,
|
||||
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL",
|
||||
replacement: {
|
||||
match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.UserSettingsSections\).*?(\i)\.default\.open\()/,
|
||||
replace: "$2.default.open($1);return;"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -146,12 +214,67 @@ export default definePlugin({
|
|||
ColorPicker = e;
|
||||
},
|
||||
|
||||
isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) {
|
||||
const firstChild = settings?.[0];
|
||||
// lowest two elements... sanity backup
|
||||
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true;
|
||||
|
||||
const settingsLocation = "belowNitro";
|
||||
|
||||
if (!header) return;
|
||||
|
||||
const names = {
|
||||
top: i18n.Messages.USER_SETTINGS,
|
||||
aboveNitro: i18n.Messages.BILLING_SETTINGS,
|
||||
belowNitro: i18n.Messages.APP_SETTINGS,
|
||||
aboveActivity: i18n.Messages.ACTIVITY_SETTINGS
|
||||
};
|
||||
return header === names[settingsLocation];
|
||||
},
|
||||
|
||||
patchedSettings: new WeakSet(),
|
||||
|
||||
addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: Record<string, unknown>) {
|
||||
if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return;
|
||||
|
||||
this.patchedSettings.add(elements);
|
||||
|
||||
elements.push(...this.makeSettingsCategories(sectionTypes));
|
||||
},
|
||||
|
||||
makeSettingsCategories(SectionTypes: Record<string, unknown>) {
|
||||
const { headerText, header } = findByProps("headerText", "header", "separator");
|
||||
return [
|
||||
{
|
||||
section: SectionTypes.HEADER,
|
||||
section: SectionTypes.CUSTOM,
|
||||
label: "Discord Colorways",
|
||||
className: "vc-settings-header"
|
||||
className: "vc-settings-header",
|
||||
element: () => <div className={header} style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
padding: "6px 10px"
|
||||
}}>
|
||||
<Heading
|
||||
variant="eyebrow"
|
||||
className={headerText}
|
||||
style={{
|
||||
"text-wrap": "wrap",
|
||||
color: "var(--channels-default)"
|
||||
} as CSSProperties}
|
||||
>
|
||||
Discord Colorways
|
||||
</Heading>
|
||||
<Heading
|
||||
variant="eyebrow"
|
||||
className={headerText}
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
color: "var(--channels-default)"
|
||||
}}
|
||||
>
|
||||
v{(Plugins.plugins.DiscordColorways as any).pluginVersion}
|
||||
</Heading>
|
||||
</div>
|
||||
},
|
||||
{
|
||||
section: "ColorwaysSelector",
|
||||
|
@ -165,6 +288,12 @@ export default definePlugin({
|
|||
element: SettingsPage,
|
||||
className: "dc-colorway-settings"
|
||||
},
|
||||
{
|
||||
section: "ColorwaysSourceManager",
|
||||
label: "Sources",
|
||||
element: SourceManager,
|
||||
className: "dc-colorway-sources-manager"
|
||||
},
|
||||
{
|
||||
section: "ColorwaysOnDemand",
|
||||
label: "On-Demand",
|
||||
|
@ -172,10 +301,10 @@ export default definePlugin({
|
|||
className: "dc-colorway-ondemand"
|
||||
},
|
||||
{
|
||||
section: "ColorwaysManagement",
|
||||
label: "Manage...",
|
||||
element: ManageColorwaysPage,
|
||||
className: "dc-colorway-management"
|
||||
section: "ColorwaysStore",
|
||||
label: "Store",
|
||||
element: Store,
|
||||
className: "dc-colorway-store"
|
||||
},
|
||||
{
|
||||
section: SectionTypes.DIVIDER
|
||||
|
@ -189,19 +318,98 @@ export default definePlugin({
|
|||
addServerListElement(ServerListRenderPosition.In, this.ColorwaysButton);
|
||||
|
||||
enableStyle(style);
|
||||
ColorwayCSS.set((await DataStore.get("actveColorway")) || "");
|
||||
ColorwayCSS.set((await DataStore.get("activeColorwayObject") as ColorwayObject).css || "");
|
||||
|
||||
addAccessory("colorways-btn", props => String(props.message.content).match(/colorway:[0-9a-f]{0,100}/) ? <Button
|
||||
onClick={() => openModal(modalProps => <CreatorModal
|
||||
modalProps={modalProps}
|
||||
colorwayID={String(props.message.content).match(/colorway:[0-9a-f]{0,100}/)![0]}
|
||||
/>)}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.OUTLINED}
|
||||
>
|
||||
Add this Colorway...
|
||||
</Button> : null);
|
||||
addAccessory("colorways-btn", props => {
|
||||
if (String(props.message.content).match(/colorway:[0-9a-f]{0,100}/)) {
|
||||
return <Flex flexDirection="column">
|
||||
{String(props.message.content).match(/colorway:[0-9a-f]{0,100}/g)?.map((colorID: string) => {
|
||||
colorID = hexToString(colorID.split("colorway:")[1]);
|
||||
return <div className="colorwayMessage">
|
||||
<div className="discordColorwayPreviewColorContainer" style={{ width: "56px", height: "56px", marginRight: "16px" }}>
|
||||
{(() => {
|
||||
if (colorID) {
|
||||
if (!colorID.includes(",")) {
|
||||
throw new Error("Invalid Colorway ID");
|
||||
} else {
|
||||
return colorID.split("|").filter(string => string.includes(",#"))[0].split(/,#/).map((color: string) => <div className="discordColorwayPreviewColor" style={{ backgroundColor: `#${colorToHex(color)}` }} />);
|
||||
}
|
||||
} else return null;
|
||||
})()}
|
||||
</div>
|
||||
<div className="colorwayMessage-contents">
|
||||
<Forms.FormTitle>Colorway{/n:([A-Za-z0-9]+( [A-Za-z0-9]+)+)/i.exec(colorID) ? `: ${/n:([A-Za-z0-9]+( [A-Za-z0-9]+)+)/i.exec(colorID)![1]}` : ""}</Forms.FormTitle>
|
||||
<Flex>
|
||||
<Button
|
||||
onClick={() => openModal(modalProps => <CreatorModal
|
||||
modalProps={modalProps}
|
||||
colorwayID={colorID}
|
||||
/>)}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.FILLED}
|
||||
>
|
||||
Add this Colorway...
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
Clipboard.copy(colorID);
|
||||
Toasts.show({
|
||||
message: "Copied Colorway ID Successfully",
|
||||
type: 1,
|
||||
id: "copy-colorway-id-notify",
|
||||
});
|
||||
}}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.FILLED}
|
||||
>
|
||||
Copy Colorway ID
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (!hexToString(colorID).includes(",")) {
|
||||
throw new Error("Invalid Colorway ID");
|
||||
} else {
|
||||
DataStore.set("activeColorwayObject", {
|
||||
id: "Temporary Colorway", css: generateCss(
|
||||
colorToHex(hexToString(colorID).split(/,#/)[1]),
|
||||
colorToHex(hexToString(colorID).split(/,#/)[2]),
|
||||
colorToHex(hexToString(colorID).split(/,#/)[3]),
|
||||
colorToHex(hexToString(colorID).split(/,#/)[0]),
|
||||
true,
|
||||
true,
|
||||
undefined,
|
||||
"Temporary Colorway"
|
||||
), sourceType: "temporary", source: null
|
||||
});
|
||||
ColorwayCSS.set(generateCss(
|
||||
colorToHex(hexToString(colorID).split(/,#/)[1]),
|
||||
colorToHex(hexToString(colorID).split(/,#/)[2]),
|
||||
colorToHex(hexToString(colorID).split(/,#/)[3]),
|
||||
colorToHex(hexToString(colorID).split(/,#/)[0]),
|
||||
true,
|
||||
true,
|
||||
undefined,
|
||||
"Temporary Colorway"
|
||||
));
|
||||
}
|
||||
}}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.FILLED}
|
||||
>
|
||||
Apply temporarily
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
</div>;
|
||||
})}
|
||||
</Flex>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
},
|
||||
stop() {
|
||||
removeServerListElement(ServerListRenderPosition.In, this.ColorwaysButton);
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -17,9 +17,10 @@ export interface Colorway {
|
|||
authorID: string,
|
||||
colors?: string[],
|
||||
isGradient?: boolean,
|
||||
sourceUrl?: string,
|
||||
sourceName?: string,
|
||||
linearGradient?: string;
|
||||
sourceType?: "online" | "offline" | "temporary" | null,
|
||||
source?: string,
|
||||
linearGradient?: string,
|
||||
preset?: string;
|
||||
}
|
||||
|
||||
export interface ColorPickerProps {
|
||||
|
@ -29,3 +30,35 @@ export interface ColorPickerProps {
|
|||
label: any;
|
||||
onChange(color: number): void;
|
||||
}
|
||||
|
||||
export interface ColorwayObject {
|
||||
id: string | null,
|
||||
css: string | null,
|
||||
sourceType: "online" | "offline" | "temporary" | null,
|
||||
source: string | null | undefined;
|
||||
}
|
||||
|
||||
export interface SourceObject {
|
||||
type: "online" | "offline" | "temporary",
|
||||
source: string,
|
||||
colorways: Colorway[];
|
||||
}
|
||||
|
||||
export enum SortOptions {
|
||||
NAME_AZ = 1,
|
||||
NAME_ZA = 2,
|
||||
SOURCE_AZ = 3,
|
||||
SOURCE_ZA = 4
|
||||
}
|
||||
|
||||
export interface StoreObject {
|
||||
sources: StoreItem[];
|
||||
}
|
||||
|
||||
export interface StoreItem {
|
||||
name: string,
|
||||
id: string,
|
||||
description: string,
|
||||
url: string,
|
||||
authorGh: string;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue