updated DiscordColorways to v5.6.4.2

This commit is contained in:
Seaswimmer 2024-04-27 13:51:49 -04:00
parent 101e25329e
commit cba15d56d3
Signed by: cswimr
GPG key ID: 1EBC234EEDA901AE
16 changed files with 665 additions and 1274 deletions

View file

@ -19,202 +19,73 @@ import {
useState, useState,
} from "@webpack/common"; } from "@webpack/common";
import { mainColors } from "../constants";
import { colorVariables } from "../css"; import { colorVariables } from "../css";
import { getHex } from "../utils";
interface ToolboxItem {
title: string;
onClick: () => void;
id?: string;
iconClassName?: string;
}
const ColorVarItems: ToolboxItem[] = colorVariables.map((colorVariable: string) => {
return {
title: "Copy " + colorVariable,
onClick: () => {
function getHex(str: string): string { return Object.assign(document.createElement("canvas").getContext("2d") as {}, { fillStyle: str }).fillStyle; }
Clipboard.copy(getHex(getComputedStyle(document.body).getPropertyValue("--" + colorVariable)));
Toasts.show({ message: "Color " + colorVariable + " copied to clipboard", id: "toolbox-color-var-copied", type: 1 });
},
id: colorVariable
};
});
const ToolboxItems: ToolboxItem[] = [
{
title: "Copy Accent Color",
onClick: () => {
function getHex(str: string): string {
return Object.assign(
document.createElement("canvas").getContext("2d") as {},
{ fillStyle: str }
).fillStyle;
}
Clipboard.copy(
getHex(
getComputedStyle(document.body).getPropertyValue(
"--brand-experiment"
)
)
);
Toasts.show({
message: "Accent color copied to clipboard",
id: "toolbox-accent-color-copied",
type: 1,
});
},
id: "colorways-toolbox_copy-accent",
iconClassName: "copy",
},
{
title: "Copy Primary Color",
onClick: () => {
function getHex(str: string): string {
return Object.assign(
document.createElement("canvas").getContext("2d") as {},
{ fillStyle: str }
).fillStyle;
}
Clipboard.copy(
getHex(
getComputedStyle(document.body).getPropertyValue(
"--background-primary"
)
)
);
Toasts.show({
message: "Primary color copied to clipboard",
id: "toolbox-primary-color-copied",
type: 1,
});
},
id: "colorways-toolbox_copy-primary",
iconClassName: "copy",
},
{
title: "Copy Secondary Color",
onClick: () => {
function getHex(str: string): string {
return Object.assign(
document.createElement("canvas").getContext("2d") as {},
{ fillStyle: str }
).fillStyle;
}
Clipboard.copy(
getHex(
getComputedStyle(document.body).getPropertyValue(
"--background-secondary"
)
)
);
Toasts.show({
message: "Secondary color copied to clipboard",
id: "toolbox-secondary-color-copied",
type: 1,
});
},
id: "colorways-toolbox_copy-secondary",
iconClassName: "copy",
},
{
title: "Copy Tertiary Color",
onClick: () => {
function getHex(str: string): string {
return Object.assign(
document.createElement("canvas").getContext("2d") as {},
{ fillStyle: str }
).fillStyle;
}
Clipboard.copy(
getHex(
getComputedStyle(document.body).getPropertyValue(
"--background-tertiary"
)
)
);
Toasts.show({
message: "Tertiary color copied to clipboard",
id: "toolbox-tertiary-color-copied",
type: 1,
});
},
id: "colorways-toolbox_copy-tertiary",
iconClassName: "copy",
}
];
export default function ({ modalProps }: { modalProps: ModalProps; }) { export default function ({ modalProps }: { modalProps: ModalProps; }) {
const [colorVarItems, setColorVarItems] = useState<ToolboxItem[]>(ColorVarItems); const [ColorVars, setColorVars] = useState<string[]>(colorVariables);
const [collapsedSettings, setCollapsedSettings] = useState<boolean>(true); const [collapsedSettings, setCollapsedSettings] = useState<boolean>(true);
let results: ToolboxItem[]; let results: string[];
function searchToolboxItems(e: string) { function searchToolboxItems(e: string) {
results = []; results = [];
ColorVarItems.find((ToolboxItem: ToolboxItem) => { colorVariables.find((colorVariable: string) => {
if (ToolboxItem.title.toLowerCase().includes(e.toLowerCase())) { if (colorVariable.toLowerCase().includes(e.toLowerCase())) {
results.push(ToolboxItem); results.push(colorVariable);
} }
}); });
setColorVarItems(results); setColorVars(results);
} }
return ( return <ModalRoot {...modalProps} className="colorwayColorpicker">
<ModalRoot {...modalProps} className="colorwayColorpicker"> <Flex style={{ gap: "8px", marginBottom: "8px" }}>
<Flex style={{ gap: "8px", marginBottom: "8px" }}> <TextInput
<TextInput className="colorwaysColorpicker-search"
className="colorwaysColorpicker-search" placeholder="Search for a color:"
placeholder="Search for a color:" onChange={e => {
onChange={e => { searchToolboxItems(e);
searchToolboxItems(e); if (e) {
if (e) { setCollapsedSettings(false);
setCollapsedSettings(false); } else {
} else { setCollapsedSettings(true);
setCollapsedSettings(true); }
} }}
}} />
/> <Button
<Button innerClassName="colorwaysSettings-iconButtonInner"
innerClassName="colorwaysSettings-iconButtonInner" size={Button.Sizes.ICON}
size={Button.Sizes.ICON} color={Button.Colors.PRIMARY}
color={Button.Colors.TRANSPARENT} look={Button.Looks.OUTLINED}
onClick={() => setCollapsedSettings(!collapsedSettings)} onClick={() => setCollapsedSettings(!collapsedSettings)}
> >
<svg width="32" height="24" viewBox="0 0 24 24" aria-hidden="true" role="img"> <svg width="32" height="24" viewBox="0 0 24 24" aria-hidden="true" role="img">
<path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M7 10L12 15 17 10" aria-hidden="true" /> <path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M7 10L12 15 17 10" aria-hidden="true" />
</svg> </svg>
</Button> </Button>
</Flex> </Flex>
<ScrollerThin style={{ color: "var(--text-normal)" }} orientation="vertical" className={collapsedSettings ? " colorwaysColorpicker-collapsed" : ""} paddingFix> <ScrollerThin style={{ color: "var(--text-normal)" }} orientation="vertical" className={collapsedSettings ? " colorwaysColorpicker-collapsed" : ""} paddingFix>
{colorVarItems.map((toolboxItem: ToolboxItem) => { {ColorVars.map((colorVariable: string) => <div
return ( id={`colorways-colorstealer-item_${colorVariable}`}
<div className="colorwaysCreator-settingItm colorwaysCreator-toolboxItm"
id={ onClick={() => {
"colorways-colorstealer-item_" + Clipboard.copy(getHex(getComputedStyle(document.body).getPropertyValue("--" + colorVariable)));
toolboxItem.id Toasts.show({ message: "Color " + colorVariable + " copied to clipboard", id: "toolbox-color-var-copied", type: 1 });
} }} style={{ "--brand-experiment": `var(--${colorVariable})` } as React.CSSProperties}>
className="colorwaysCreator-settingItm colorwaysCreator-toolboxItm" {`Copy ${colorVariable}`}
onClick={toolboxItem.onClick} </div>)}
style={ </ScrollerThin>
{ <Flex style={{ justifyContent: "space-between", marginTop: "8px" }} wrap="wrap" className={collapsedSettings ? "" : " colorwaysColorpicker-collapsed"}>
"--brand-experiment": {mainColors.map(mainColor => <div
"var(--" + toolboxItem.id + ")", id={`colorways-toolbox_copy-${mainColor.name}`}
} as React.CSSProperties className="colorwayToolbox-listItem"
} >
> <CopyIcon onClick={() => {
{toolboxItem.title} Clipboard.copy(getHex(getComputedStyle(document.body).getPropertyValue(mainColor.var)));
</div> Toasts.show({ message: `${mainColor.title} color copied to clipboard`, id: `toolbox-${mainColor.name}-color-copied`, type: 1 });
); }} width={20} height={20} className="colorwayToolbox-listItemSVG" />
})} <span className="colorwaysToolbox-label">{`Copy ${mainColor.title} Color`}</span>
</ScrollerThin> </div>
<Flex style={{ justifyContent: "space-between", marginTop: "8px" }} wrap="wrap" className={collapsedSettings ? "" : " colorwaysColorpicker-collapsed"}> )}
{ToolboxItems.map((toolboxItem: ToolboxItem, i: number) => <div </Flex>
id={toolboxItem.id || `colorways-toolbox_item-${i}`} </ModalRoot>;
className="colorwayToolbox-listItem"
>
<CopyIcon onClick={toolboxItem.onClick} width={20} height={20} className="colorwayToolbox-listItemSVG" />
<span className="colorwaysToolbox-label">{toolboxItem.title}</span>
</div>
)}
</Flex>
</ModalRoot>
);
} }

View file

@ -6,11 +6,11 @@
import * as DataStore from "@api/DataStore"; import * as DataStore from "@api/DataStore";
import { openModal } from "@utils/modal"; import { openModal } from "@utils/modal";
import { FluxDispatcher, Text, Tooltip, useCallback, useEffect, useState } from "@webpack/common"; import { FluxDispatcher, Text, Tooltip, useEffect, useState } from "@webpack/common";
import { FluxEvents } from "@webpack/types"; import { FluxEvents } from "@webpack/types";
import { PalleteIcon } from "./Icons"; import { PalleteIcon } from "./Icons";
import SelectorModal from "./SelectorModal"; import Selector from "./Selector";
export default function ({ export default function ({
listItemClass = "ColorwaySelectorBtnContainer", listItemClass = "ColorwaySelectorBtnContainer",
@ -34,10 +34,8 @@ export default function ({
setIsThin(useThinMenuButton); setIsThin(useThinMenuButton);
} }
const cached_setButtonVisibility = useCallback(setButtonVisibility, []);
useEffect(() => { useEffect(() => {
cached_setButtonVisibility(); setButtonVisibility();
}); });
FluxDispatcher.subscribe("COLORWAYS_UPDATE_BUTTON_HEIGHT" as FluxEvents, ({ isTall }) => { FluxDispatcher.subscribe("COLORWAYS_UPDATE_BUTTON_HEIGHT" as FluxEvents, ({ isTall }) => {
@ -48,61 +46,23 @@ export default function ({
setVisibility(isVisible); setVisibility(isVisible);
}); });
if (!isThin) { return <Tooltip text={
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>
<span>Colorways</span> } position="right" tooltipContentClassName="colorwaysBtn-tooltipContent"
<Text variant="text-xs/normal" style={{ color: "var(--text-muted)", fontWeight: 500 }}>{"Active Colorway: " + activeColorway}</Text> >
</>} position="right" tooltipContentClassName={listItemTooltipClass} {({ onMouseEnter, onMouseLeave, onClick }) => visibility ? <div className="ColorwaySelectorBtnContainer">
> <div
{({ onMouseEnter, onMouseLeave, onClick }) => { className={"ColorwaySelectorBtn" + (isThin ? " ColorwaySelectorBtn_thin" : "")}
return ( onMouseEnter={async () => {
<> onMouseEnter();
{visibility && <div className={listItemClass}> setActiveColorway(await DataStore.get("actveColorwayID") || "None");
<div }}
className={listItemWrapperClass + " ColorwaySelectorBtn"} onMouseLeave={onMouseLeave}
onMouseEnter={async () => { onClick={() => {
onMouseEnter(); onClick();
setActiveColorway(await DataStore.get("actveColorwayID") || "None"); openModal((props: any) => <Selector modalProps={props} />);
}} }}
onMouseLeave={onMouseLeave} >{isThin ? <Text variant="text-xs/normal" style={{ color: "var(--header-primary)", fontWeight: 700, fontSize: 9 }}>Colorways</Text> : <PalleteIcon />}</div>
onClick={() => { </div> : <></>}
onClick(); </Tooltip>;
openModal(props => <SelectorModal modalProps={props} />);
}}
><PalleteIcon /></div>
</div>}
</>
);
}}
</Tooltip>
);
} else {
return (<Tooltip text={<>
<span>Colorways</span>
<Text variant="text-xs/normal" style={{ color: "var(--text-muted)", fontWeight: 500 }}>{"Active Colorway: " + activeColorway}</Text>
</>} position="right" tooltipContentClassName={listItemTooltipClass}
>
{({ onMouseEnter, onMouseLeave, onClick }) => {
return (
<>
{visibility && <div className={listItemClass}>
<div
className={listItemWrapperClass + " ColorwaySelectorBtn ColorwaySelectorBtn_thin"}
onMouseEnter={async () => {
onMouseEnter();
setActiveColorway(await DataStore.get("actveColorwayID") || "None");
}}
onMouseLeave={onMouseLeave}
onClick={() => {
onClick();
openModal(props => <SelectorModal modalProps={props} />);
}}
><Text variant="text-xs/normal" style={{ color: "var(--header-primary)", fontWeight: 700, fontSize: 9 }}>Colorways</Text></div>
</div>}
</>
);
}}
</Tooltip>
);
}
} }

View file

@ -31,6 +31,7 @@ import { generateCss, getPreset } from "../css";
import { Colorway } from "../types"; import { Colorway } from "../types";
import { colorToHex, getHex, hexToString } from "../utils"; import { colorToHex, getHex, hexToString } from "../utils";
import ConflictingColorsModal from "./ConflictingColorsModal"; import ConflictingColorsModal from "./ConflictingColorsModal";
import InputColorwayIdModal from "./InputColorwayIdModal";
import ThemePreviewCategory from "./ThemePreview"; import ThemePreviewCategory from "./ThemePreview";
export default function ({ export default function ({
modalProps, modalProps,
@ -51,26 +52,46 @@ export default function ({
const [collapsedSettings, setCollapsedSettings] = useState<boolean>(true); const [collapsedSettings, setCollapsedSettings] = useState<boolean>(true);
const [collapsedPresets, setCollapsedPresets] = useState<boolean>(true); const [collapsedPresets, setCollapsedPresets] = useState<boolean>(true);
const [preset, setPreset] = useState<string>("default"); const [preset, setPreset] = useState<string>("default");
const [presetColorArray, setPresetColorArray] = useState<string[]>(["accent", "primary", "secondary", "tertiary"]); const [presetColorArray, setPresetColorArray] = useState<string[]>(["primary", "secondary", "tertiary", "accent"]);
const colorProps = {
accent: {
get: accentColor,
set: setAccentColor,
name: "Accent"
},
primary: {
get: primaryColor,
set: setPrimaryColor,
name: "Primary"
},
secondary: {
get: secondaryColor,
set: setSecondaryColor,
name: "Secondary"
},
tertiary: {
get: tertiaryColor,
set: setTertiaryColor,
name: "Tertiary"
}
};
useEffect(() => { useEffect(() => {
const parsedID = colorwayID?.split("colorway:")[1]; const parsedID = colorwayID?.split("colorway:")[1];
if (parsedID) { if (parsedID) {
const allEqual = (arr: any[]) => arr.every(v => v === arr[0]);
if (!parsedID) { if (!parsedID) {
throw new Error("Please enter a Colorway ID"); throw new Error("Please enter a Colorway ID");
} else if (parsedID.length < 62) {
throw new Error("Invalid Colorway ID");
} else if (!hexToString(parsedID).includes(",")) { } else if (!hexToString(parsedID).includes(",")) {
throw new Error("Invalid Colorway ID"); throw new Error("Invalid Colorway ID");
} else if (!allEqual(hexToString(parsedID).split(",").map((e: string) => e.match("#")!.length)) && hexToString(parsedID).split(",").map((e: string) => e.match("#")!.length)[0] !== 1) {
throw new Error("Invalid Colorway ID");
} else { } else {
const colorArray: string[] = hexToString(parsedID).split(","); const setColor = [
setAccentColor(colorArray[0].split("#")[1]); setAccentColor,
setPrimaryColor(colorArray[1].split("#")[1]); setPrimaryColor,
setSecondaryColor(colorArray[2].split("#")[1]); setSecondaryColor,
setTertiaryColor(colorArray[3].split("#")[1]); setTertiaryColor
];
hexToString(parsedID).split(/,#/).forEach((color: string, i: number) => setColor[i](colorToHex(color)));
} }
} }
}); });
@ -100,62 +121,26 @@ export default function ({
value={colorwayName} value={colorwayName}
onChange={setColorwayName} onChange={setColorwayName}
/> />
<Forms.FormTitle style={{ marginBottom: 0 }}> <div className="colorwaysCreator-settingCat">
Colors: <Forms.FormTitle style={{ marginBottom: 0 }}>
</Forms.FormTitle> Colors:
<div className="colorwayCreator-colorPreviews"> </Forms.FormTitle>
{presetColorArray.includes("primary") && <div className="colorwayCreator-colorPreviews">
<ColorPicker {presetColorArray.map(presetColor => {
label={<Text className="colorwaysPicker-colorLabel">Primary</Text>} return <ColorPicker
color={parseInt(primaryColor, 16)} label={<Text className="colorwaysPicker-colorLabel">{colorProps[presetColor].name}</Text>}
onChange={(color: number) => { color={parseInt(colorProps[presetColor].get, 16)}
let hexColor = color.toString(16); onChange={(color: number) => {
while (hexColor.length < 6) { let hexColor = color.toString(16);
hexColor = "0" + hexColor; while (hexColor.length < 6) {
} hexColor = "0" + hexColor;
setPrimaryColor(hexColor); }
}} colorProps[presetColor].set(hexColor);
{...colorPickerProps} }}
/>} {...colorPickerProps}
{presetColorArray.includes("secondary") && />;
<ColorPicker })}
label={<Text className="colorwaysPicker-colorLabel">Secondary</Text>} </div>
color={parseInt(secondaryColor, 16)}
onChange={(color: number) => {
let hexColor = color.toString(16);
while (hexColor.length < 6) {
hexColor = "0" + hexColor;
}
setSecondaryColor(hexColor);
}}
{...colorPickerProps}
/>}
{presetColorArray.includes("tertiary") &&
<ColorPicker
label={<Text className="colorwaysPicker-colorLabel">Tertiary</Text>}
color={parseInt(tertiaryColor, 16)}
onChange={(color: number) => {
let hexColor = color.toString(16);
while (hexColor.length < 6) {
hexColor = "0" + hexColor;
}
setTertiaryColor(hexColor);
}}
{...colorPickerProps}
/>}
{presetColorArray.includes("accent") &&
<ColorPicker
label={<Text className="colorwaysPicker-colorLabel">Accent</Text>}
color={parseInt(accentColor, 16)}
onChange={(color: number) => {
let hexColor = color.toString(16);
while (hexColor.length < 6) {
hexColor = "0" + hexColor;
}
setAccentColor(hexColor);
}}
{...colorPickerProps}
/>}
</div> </div>
<div className={`colorwaysCreator-settingCat${collapsedSettings ? " colorwaysCreator-settingCat-collapsed" : ""}`}> <div className={`colorwaysCreator-settingCat${collapsedSettings ? " colorwaysCreator-settingCat-collapsed" : ""}`}>
<div <div
@ -231,12 +216,18 @@ export default function ({
discordSaturation discordSaturation
); );
} else { } else {
customColorwayCSS = getPreset( (getPreset()[preset].id === "gradientType1" || getPreset()[preset].id === "gradientType2") ?
primaryColor, customColorwayCSS = getPreset(
secondaryColor, primaryColor,
tertiaryColor, secondaryColor,
accentColor tertiaryColor,
)[preset].preset(discordSaturation); accentColor
)[preset].preset(discordSaturation).full : customColorwayCSS = getPreset(
primaryColor,
secondaryColor,
tertiaryColor,
accentColor
)[preset].preset(discordSaturation);
} }
const customColorway: Colorway = { const customColorway: Colorway = {
name: (colorwayName || "Colorway") + (preset === "default" ? "" : ": Made for " + getPreset()[preset].name), name: (colorwayName || "Colorway") + (preset === "default" ? "" : ": Made for " + getPreset()[preset].name),
@ -248,6 +239,13 @@ export default function ({
colors: presetColorArray, colors: presetColorArray,
author: UserStore.getCurrentUser().username, author: UserStore.getCurrentUser().username,
authorID: UserStore.getCurrentUser().id, authorID: UserStore.getCurrentUser().id,
isGradient: getPreset()[preset].id === "gradientType1" || getPreset()[preset].id === "gradientType2",
linearGradient: (getPreset()[preset].id === "gradientType1" || getPreset()[preset].id === "gradientType2") ? getPreset(
primaryColor,
secondaryColor,
tertiaryColor,
accentColor
)[preset].preset(discordSaturation).base : null
}; };
const customColorwaysArray: Colorway[] = [customColorway]; const customColorwaysArray: Colorway[] = [customColorway];
DataStore.get("customColorways").then( DataStore.get("customColorways").then(
@ -270,7 +268,7 @@ export default function ({
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
size={Button.Sizes.MEDIUM} size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED} look={Button.Looks.OUTLINED}
onClick={() => { onClick={() => {
function setAllColors({ accent, primary, secondary, tertiary }: { accent: string, primary: string, secondary: string, tertiary: string; }) { function setAllColors({ accent, primary, secondary, tertiary }: { accent: string, primary: string, secondary: string, tertiary: string; }) {
setAccentColor(accent.split("#")[1]); setAccentColor(accent.split("#")[1]);
@ -324,60 +322,16 @@ export default function ({
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
size={Button.Sizes.MEDIUM} size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED} look={Button.Looks.OUTLINED}
onClick={() => { onClick={() => openModal((props: any) => <InputColorwayIdModal modalProps={props} onColorwayId={colorwayID => {
let colorwayID: string; const setColor = [
function setColorwayID(e: string) { setAccentColor,
colorwayID = e; setPrimaryColor,
} setSecondaryColor,
openModal(props => { setTertiaryColor
return ( ];
<ModalRoot {...props} className="colorwaysCreator-noMinHeight"> hexToString(colorwayID).split(/,#/).forEach((color: string, i: number) => setColor[i](colorToHex(color)));
<ModalContent className="colorwaysCreator-noHeader colorwaysCreator-noMinHeight"> }} />)}
<Forms.FormTitle>Colorway ID:</Forms.FormTitle>
<TextInput placeholder="Enter Colorway ID" onInput={e => setColorwayID(e.currentTarget.value)} />
</ModalContent>
<ModalFooter>
<Button
style={{ marginLeft: 8 }}
color={Button.Colors.BRAND}
size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED}
onClick={() => {
if (!colorwayID) {
throw new Error("Please enter a Colorway ID");
} else if (!hexToString(colorwayID).includes(",")) {
throw new Error("Invalid Colorway ID");
} else {
const setColor = [
setAccentColor,
setPrimaryColor,
setSecondaryColor,
setTertiaryColor
];
hexToString(colorwayID).split(/,#/).forEach((color: string, i: number) => setColor[i](colorToHex(color)));
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>
);
});
}}
> >
Enter Colorway ID Enter Colorway ID
</Button> </Button>
@ -385,7 +339,7 @@ export default function ({
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
size={Button.Sizes.MEDIUM} size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED} look={Button.Looks.OUTLINED}
onClick={() => { onClick={() => {
modalProps.onClose(); modalProps.onClose();
}} }}

View file

@ -1,15 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
export default () => {
return <div style={{
width: "100%",
height: 1,
borderTop: "thin solid var(--background-modifier-accent)",
marginTop: "var(--custom-margin-margin-medium)",
marginBottom: 20
}} />;
};

View file

@ -140,7 +140,7 @@ export default function ({
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
size={Button.Sizes.MEDIUM} size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED} look={Button.Looks.OUTLINED}
onClick={() => { onClick={() => {
const stringToHex = (str: string) => { const stringToHex = (str: string) => {
let hex = ""; let hex = "";
@ -169,7 +169,7 @@ export default function ({
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
size={Button.Sizes.MEDIUM} size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED} look={Button.Looks.OUTLINED}
onClick={() => { onClick={() => {
Clipboard.copy(colorwayProps["dc-import"]); Clipboard.copy(colorwayProps["dc-import"]);
Toasts.show({ Toasts.show({
@ -185,7 +185,7 @@ export default function ({
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
size={Button.Sizes.MEDIUM} size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED} look={Button.Looks.OUTLINED}
onClick={async () => { onClick={async () => {
const customColorways = await DataStore.get("customColorways"); const customColorways = await DataStore.get("customColorways");
const customColorwaysArray: Colorway[] = []; const customColorwaysArray: Colorway[] = [];
@ -211,7 +211,7 @@ export default function ({
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
size={Button.Sizes.MEDIUM} size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED} look={Button.Looks.OUTLINED}
onClick={async () => { onClick={async () => {
const colorwaySourceFiles = await DataStore.get( const colorwaySourceFiles = await DataStore.get(
"colorwaySourceFiles" "colorwaySourceFiles"
@ -251,7 +251,7 @@ export default function ({
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
size={Button.Sizes.MEDIUM} size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED} look={Button.Looks.OUTLINED}
onClick={() => { onClick={() => {
modalProps.onClose(); modalProps.onClose();
}} }}

View file

@ -0,0 +1,49 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { ModalContent, ModalFooter, ModalProps, ModalRoot } from "@utils/modal";
import { Button, Forms, TextInput, useState } from "@webpack/common";
import { hexToString } from "../utils";
export default function ({ modalProps, onColorwayId }: { modalProps: ModalProps, onColorwayId: (colorwayID: string) => void; }) {
const [colorwayID, setColorwayID] = useState<string>("");
return <ModalRoot {...modalProps} className="colorwaysCreator-noMinHeight">
<ModalContent className="colorwaysCreator-noHeader colorwaysCreator-noMinHeight">
<Forms.FormTitle>Colorway ID:</Forms.FormTitle>
<TextInput placeholder="Enter Colorway ID" onInput={e => setColorwayID(e.currentTarget.value)} />
</ModalContent>
<ModalFooter>
<Button
style={{ marginLeft: 8 }}
color={Button.Colors.BRAND}
size={Button.Sizes.MEDIUM}
look={Button.Looks.FILLED}
onClick={() => {
if (!colorwayID) {
throw new Error("Please enter a Colorway ID");
} else if (!hexToString(colorwayID).includes(",")) {
throw new Error("Invalid Colorway ID");
} else {
onColorwayId(colorwayID);
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>;
}

View file

@ -7,6 +7,8 @@
/* eslint-disable arrow-parens */ /* eslint-disable arrow-parens */
import * as DataStore from "@api/DataStore"; import * as DataStore from "@api/DataStore";
import { Flex } from "@components/Flex";
import { SettingsTab } from "@components/VencordSettings/shared";
import { ModalContent, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; import { ModalContent, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
import { findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { import {
@ -23,10 +25,11 @@ import {
useEffect, useEffect,
useState, useState,
} from "@webpack/common"; } from "@webpack/common";
import { ReactNode } from "react";
import { ColorwayCSS } from ".."; import { ColorwayCSS } from "..";
import { defaultColorwaySource, fallbackColorways } from "../constants"; import { defaultColorwaySource, fallbackColorways } from "../constants";
import { generateCss } from "../css"; import { generateCss, gradientBase } from "../css";
import { Colorway } from "../types"; import { Colorway } from "../types";
import { colorToHex } from "../utils"; import { colorToHex } from "../utils";
import ColorPickerModal from "./ColorPicker"; import ColorPickerModal from "./ColorPicker";
@ -36,10 +39,46 @@ import ColorwayInfoModal from "./InfoModal";
const { SelectionCircle } = findByPropsLazy("SelectionCircle"); const { SelectionCircle } = findByPropsLazy("SelectionCircle");
function SelectorContainer({ children, isSettings, modalProps }: { children: ReactNode, isSettings?: boolean, modalProps: ModalProps; }) {
if (!isSettings) {
return <ModalRoot {...modalProps} className="colorwaySelectorModal">
{children}
</ModalRoot>;
} else {
return <SettingsTab title="Colors">
<div className="colorwaysSettingsSelector-wrapper">
{children}
</div>
</SettingsTab>;
}
}
function SelectorHeader({ children, isSettings }: { children: ReactNode, isSettings?: boolean; }) {
if (!isSettings) {
return <ModalHeader>
{children}
</ModalHeader>;
} else {
return <Flex style={{ gap: "0" }}>
{children}
</Flex>;
}
}
function SelectorContent({ children, isSettings }: { children: ReactNode, isSettings?: boolean; }) {
if (!isSettings) {
return <ModalContent className="colorwaySelectorModalContent">{children}</ModalContent>;
} else {
return <>{children}</>;
}
}
export default function ({ export default function ({
modalProps, modalProps,
isSettings
}: { }: {
modalProps: ModalProps; modalProps: ModalProps,
isSettings?: boolean;
}): JSX.Element | any { }): JSX.Element | any {
const [currentColorway, setCurrentColorway] = useState<string>(""); const [currentColorway, setCurrentColorway] = useState<string>("");
const [colorways, setColorways] = useState<Colorway[]>([]); const [colorways, setColorways] = useState<Colorway[]>([]);
@ -179,8 +218,8 @@ export default function ({
} }
return ( return (
<ModalRoot {...modalProps} className="colorwaySelectorModal"> <SelectorContainer modalProps={modalProps} isSettings={isSettings}>
<ModalHeader> <SelectorHeader isSettings={isSettings}>
<Select className="colorwaySelector-pill colorwaySelector-pill_select" options={[{ <Select className="colorwaySelector-pill colorwaySelector-pill_select" options={[{
value: "all", value: "all",
label: "All" label: "All"
@ -221,6 +260,7 @@ export default function ({
innerClassName="colorwaysSettings-iconButtonInner" innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON} size={Button.Sizes.ICON}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
look={Button.Looks.OUTLINED}
style={{ marginLeft: "8px" }} style={{ marginLeft: "8px" }}
id="colorway-refreshcolorway" id="colorway-refreshcolorway"
onMouseEnter={isShown ? () => { } : onMouseEnter} onMouseEnter={isShown ? () => { } : onMouseEnter}
@ -237,7 +277,7 @@ export default function ({
y="0px" y="0px"
width="20" width="20"
height="20" height="20"
style={{ padding: "6px" }} style={{ padding: "6px", boxSizing: "content-box" }}
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="currentColor" fill="currentColor"
> >
@ -255,11 +295,12 @@ export default function ({
</Popout>; </Popout>;
}} }}
</Tooltip> </Tooltip>
<Tooltip text="Open Settings"> {!isSettings ? <Tooltip text="Open Settings">
{({ onMouseEnter, onMouseLeave }) => <Button {({ onMouseEnter, onMouseLeave }) => <Button
innerClassName="colorwaysSettings-iconButtonInner" innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON} size={Button.Sizes.ICON}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
look={Button.Looks.OUTLINED}
style={{ marginLeft: "8px" }} style={{ marginLeft: "8px" }}
id="colorway-opensettings" id="colorway-opensettings"
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
@ -274,18 +315,19 @@ export default function ({
role="img" role="img"
width="20" width="20"
height="20" height="20"
style={{ padding: "6px" }} style={{ padding: "6px", boxSizing: "content-box" }}
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M19.738 10H22V14H19.739C19.498 14.931 19.1 15.798 18.565 16.564L20 18L18 20L16.565 18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069 19.498 8.203 19.099 7.436 18.564L6 20L4 18L5.436 16.564C4.901 15.799 4.502 14.932 4.262 14H2V10H4.262C4.502 9.068 4.9 8.202 5.436 7.436L4 6L6 4L7.436 5.436C8.202 4.9 9.068 4.502 10 4.262V2H14V4.261C14.932 4.502 15.797 4.9 16.565 5.435L18 3.999L20 5.999L18.564 7.436C19.099 8.202 19.498 9.069 19.738 10ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z" /> <path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M19.738 10H22V14H19.739C19.498 14.931 19.1 15.798 18.565 16.564L20 18L18 20L16.565 18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069 19.498 8.203 19.099 7.436 18.564L6 20L4 18L5.436 16.564C4.901 15.799 4.502 14.932 4.262 14H2V10H4.262C4.502 9.068 4.9 8.202 5.436 7.436L4 6L6 4L7.436 5.436C8.202 4.9 9.068 4.502 10 4.262V2H14V4.261C14.932 4.502 15.797 4.9 16.565 5.435L18 3.999L20 5.999L18.564 7.436C19.099 8.202 19.498 9.069 19.738 10ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z" />
</svg> </svg>
</Button>} </Button>}
</Tooltip> </Tooltip> : <></>}
<Tooltip text="Create Colorway..."> <Tooltip text="Create Colorway...">
{({ onMouseEnter, onMouseLeave }) => <Button {({ onMouseEnter, onMouseLeave }) => <Button
innerClassName="colorwaysSettings-iconButtonInner" innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON} size={Button.Sizes.ICON}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
look={Button.Looks.OUTLINED}
style={{ marginLeft: "8px" }} style={{ marginLeft: "8px" }}
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
@ -300,7 +342,7 @@ export default function ({
role="img" role="img"
width="20" width="20"
height="20" height="20"
style={{ padding: "6px" }} style={{ padding: "6px", boxSizing: "content-box" }}
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
<path <path
@ -315,32 +357,34 @@ export default function ({
innerClassName="colorwaysSettings-iconButtonInner" innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON} size={Button.Sizes.ICON}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
look={Button.Looks.OUTLINED}
style={{ marginLeft: "8px" }} style={{ marginLeft: "8px" }}
id="colorway-opencolorstealer" id="colorway-opencolorstealer"
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
onClick={() => openModal((props) => <ColorPickerModal modalProps={props} />)} onClick={() => openModal((props) => <ColorPickerModal modalProps={props} />)}
> >
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" style={{ padding: "6px" }} fill="currentColor" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" style={{ padding: "6px", boxSizing: "content-box" }} fill="currentColor" viewBox="0 0 16 16">
<path d="M12.433 10.07C14.133 10.585 16 11.15 16 8a8 8 0 1 0-8 8c1.996 0 1.826-1.504 1.649-3.08-.124-1.101-.252-2.237.351-2.92.465-.527 1.42-.237 2.433.07zM8 5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm4.5 3a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM5 6.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm.5 6.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z" /> <path d="M12.433 10.07C14.133 10.585 16 11.15 16 8a8 8 0 1 0-8 8c1.996 0 1.826-1.504 1.649-3.08-.124-1.101-.252-2.237.351-2.92.465-.527 1.42-.237 2.433.07zM8 5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm4.5 3a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM5 6.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm.5 6.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z" />
</svg> </svg>
</Button>} </Button>}
</Tooltip> </Tooltip>
<Tooltip text="Close"> {!isSettings ? <Tooltip text="Close">
{({ onMouseEnter, onMouseLeave }) => <Button {({ onMouseEnter, onMouseLeave }) => <Button
innerClassName="colorwaysSettings-iconButtonInner" innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON} size={Button.Sizes.ICON}
color={Button.Colors.PRIMARY} color={Button.Colors.PRIMARY}
look={Button.Looks.OUTLINED}
id="colorwaySelector-pill_closeSelector" id="colorwaySelector-pill_closeSelector"
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
onClick={() => modalProps.onClose()} onClick={() => modalProps.onClose()}
> >
<CloseIcon style={{ padding: "6px" }} width={20} height={20} /> <CloseIcon style={{ padding: "6px", boxSizing: "content-box" }} width={20} height={20} />
</Button>} </Button>}
</Tooltip> </Tooltip> : <></>}
</ModalHeader> </SelectorHeader>
<ModalContent className="colorwaySelectorModalContent"> <SelectorContent isSettings={isSettings}>
<div className="colorwaysLoader-barContainer"><div className="colorwaysLoader-bar" style={{ height: loaderHeight }} /></div> <div className="colorwaysLoader-barContainer"><div className="colorwaysLoader-bar" style={{ height: loaderHeight }} /></div>
<ScrollerThin style={{ maxHeight: "450px" }} className="ColorwaySelectorWrapper"> <ScrollerThin style={{ maxHeight: "450px" }} className="ColorwaySelectorWrapper">
{visibleColorwayArray.length === 0 && {visibleColorwayArray.length === 0 &&
@ -364,101 +408,105 @@ export default function ({
]; ];
return ( return (
<Tooltip text={color.name}> <Tooltip text={color.name}>
{({ onMouseEnter, onMouseLeave }) => <div {({ onMouseEnter, onMouseLeave }) => {
className={"discordColorway" + (currentColorway === color.name ? "" : "")} return (
id={"colorway-" + color.name} <div
data-last-official={ind + 1 === colorways.length} className="discordColorway"
onMouseEnter={onMouseEnter} id={"colorway-" + color.name}
onMouseLeave={onMouseLeave} data-last-official={
> ind + 1 === colorways.length
<div
className="colorwayInfoIconContainer"
onClick={() => openModal((props) => <ColorwayInfoModal
modalProps={props}
colorwayProps={color}
discrimProps={customColorways.includes(color)}
loadUIProps={cached_loadUI}
/>)}
>
<div className="colorwayInfoIcon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
viewBox="0 0 16 16"
>
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" />
</svg>
</div>
</div>
<div className="colorwayCheckIconContainer">
<div className="colorwayCheckIcon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="currentColor"
viewBox="0 0 24 24"
>
<circle r="8" cx="12" cy="12" fill="var(--white-500)" />
<g fill="none" fill-rule="evenodd">
<path fill="var(--brand-500)" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
</g>
</svg>
</div>
</div>
<div
className="discordColorwayPreviewColorContainer"
onClick={async () => {
const [
onDemandWays,
onDemandWaysTintedText,
onDemandWaysDiscordSaturation
] = await DataStore.getMany([
"onDemandWays",
"onDemandWaysTintedText",
"onDemandWaysDiscordSaturation"
]);
if (currentColorway === color.name) {
DataStore.set("actveColorwayID", null);
DataStore.set("actveColorway", null);
ColorwayCSS.remove();
} else {
DataStore.set("activeColorwayColors", color.colors);
DataStore.set("actveColorwayID", color.name);
if (onDemandWays) {
const demandedColorway = generateCss(
colorToHex(color.primary),
colorToHex(color.secondary),
colorToHex(color.tertiary),
colorToHex(color.accent),
onDemandWaysTintedText,
onDemandWaysDiscordSaturation
);
DataStore.set("actveColorway", demandedColorway);
ColorwayCSS.set(demandedColorway);
} else {
DataStore.set("actveColorway", color["dc-import"]);
ColorwayCSS.set(color["dc-import"]);
}
} }
setCurrentColorway(await DataStore.get("actveColorwayID") as string); onMouseEnter={onMouseEnter}
}} onMouseLeave={onMouseLeave}
> onClick={async () => {
{colors.map((colorItm) => <div const [
className="discordColorwayPreviewColor" onDemandWays,
style={{ backgroundColor: color[colorItm] }} onDemandWaysTintedText,
/>)} onDemandWaysDiscordSaturation
</div> ] = await DataStore.getMany([
{currentColorway === color.name && <SelectionCircle />} "onDemandWays",
</div>} "onDemandWaysTintedText",
"onDemandWaysDiscordSaturation"
]);
if (currentColorway === color.name) {
DataStore.set("actveColorwayID", null);
DataStore.set("actveColorway", null);
ColorwayCSS.remove();
} else {
DataStore.set("activeColorwayColors", color.colors);
DataStore.set("actveColorwayID", color.name);
if (onDemandWays) {
const demandedColorway = !color.isGradient ? generateCss(
colorToHex(color.primary),
colorToHex(color.secondary),
colorToHex(color.tertiary),
colorToHex(color.accent),
onDemandWaysTintedText,
onDemandWaysDiscordSaturation
) : gradientBase(colorToHex(color.accent), onDemandWaysDiscordSaturation) + `:root:root {--custom-theme-background: linear-gradient(${color.linearGradient})}`;
DataStore.set("actveColorway", demandedColorway);
ColorwayCSS.set(demandedColorway);
} else {
DataStore.set("actveColorway", color["dc-import"]);
ColorwayCSS.set(color["dc-import"]);
}
}
setCurrentColorway(await DataStore.get("actveColorwayID") as string);
}}
>
<div
className="colorwayInfoIconContainer"
onClick={(e) => {
e.stopPropagation();
openModal((props) => (
<ColorwayInfoModal
modalProps={
props
}
colorwayProps={
color
}
discrimProps={customColorways.includes(
color
)}
loadUIProps={cached_loadUI}
/>
));
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="currentColor"
viewBox="0 0 16 16"
>
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" />
</svg>
</div>
<div className="discordColorwayPreviewColorContainer">
{!color.isGradient ? colors.map((colorItm) => <div
className="discordColorwayPreviewColor"
style={{
backgroundColor: color[colorItm],
}}
/>) : <div
className="discordColorwayPreviewColor"
style={{
background: `linear-gradient(${color.linearGradient})`,
}}
/>}
</div>
{currentColorway === color.name && <SelectionCircle />}
</div>
);
}}
</Tooltip> </Tooltip>
); );
}) })
)} )}
</ScrollerThin> </ScrollerThin>
</ModalContent > </SelectorContent>
</ModalRoot > </SelectorContainer >
); );
} }

View file

@ -125,35 +125,5 @@ export default function () {
</Button> </Button>
</Flex> </Flex>
</Forms.FormSection> </Forms.FormSection>
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
<Forms.FormSection title="Developer Options:">
<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 colorwaysArr: Colorway[] = data.flatMap(json => json.url === defaultColorwaySource ? json.colorways : []);
colorwaysArr.forEach((color: Colorway) => {
if (IS_DISCORD_DESKTOP) {
DiscordNative.fileManager.saveWithDialog(generateCss(color.primary.split("#")[1] || "313338", color.secondary.split("#")[1] || "2b2d31", color.tertiary.split("#")[1] || "1e1f22", color.accent.split("#")[1] || "5865f2", true, true), `import_${color.name.replaceAll(" ", "-").replaceAll("'", "")}.css`);
} else {
saveFile(new File([generateCss(color.primary.split("#")[1] || "313338", color.secondary.split("#")[1] || "2b2d31", color.tertiary.split("#")[1] || "1e1f22", color.accent.split("#")[1] || "5865f2", true, true)], `import_${color.name.replaceAll(" ", "-").replaceAll("'", "")}.css`, { type: "text/plain;charset=utf-8" }));
}
});
}}>
Update all official Colorways and export
</Button>
</Forms.FormSection>
</SettingsTab>; </SettingsTab>;
} }

View file

@ -1,445 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/* eslint-disable arrow-parens */
import * as DataStore from "@api/DataStore";
import { Flex } from "@components/Flex";
import { SettingsTab } from "@components/VencordSettings/shared";
import { openModal } from "@utils/modal";
import { findByPropsLazy } from "@webpack";
import {
Button,
Forms,
Menu,
Popout,
Select,
TextInput,
Tooltip,
useCallback,
useEffect,
useState,
} from "@webpack/common";
import { ColorwayCSS } from "../..";
import { defaultColorwaySource, fallbackColorways } from "../../constants";
import { generateCss } from "../../css";
import { Colorway } from "../../types";
import { colorToHex } from "../../utils";
import ColorPickerModal from "../ColorPicker";
import CreatorModal from "../CreatorModal";
import ColorwayInfoModal from "../InfoModal";
const { SelectionCircle } = findByPropsLazy("SelectionCircle");
export default function ({
visibleTabProps = "all",
}: {
visibleTabProps?: string;
}): JSX.Element | any {
const [currentColorway, setCurrentColorway] = useState<string>("");
const [colorways, setColorways] = useState<Colorway[]>([]);
const [thirdPartyColorways, setThirdPartyColorways] = useState<Colorway[]>([]);
const [customColorways, setCustomColorways] = useState<Colorway[]>([]);
const [searchString, setSearchString] = useState<string>("");
const [loaderHeight, setLoaderHeight] = useState<string>("2px");
const [visibility, setVisibility] = useState<string>(visibleTabProps);
const [showReloadMenu, setShowReloadMenu] = useState(false);
let visibleColorwayArray: Colorway[];
switch (visibility) {
case "all":
visibleColorwayArray = [...colorways, ...thirdPartyColorways, ...customColorways];
break;
case "official":
visibleColorwayArray = [...colorways];
break;
case "3rdparty":
visibleColorwayArray = [...thirdPartyColorways];
break;
case "custom":
visibleColorwayArray = [...customColorways];
break;
default:
visibleColorwayArray = [...colorways, ...thirdPartyColorways, ...customColorways];
break;
}
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().then(dt => { return { colorways: dt.colorways, url: res.url }; }).catch(() => { return { colorways: [], url: res.url }; })
));
const colorways = data.flatMap((json) => json.url === defaultColorwaySource ? json.colorways : []);
const thirdPartyColorwaysArr = data.flatMap((json) => json.url !== defaultColorwaySource ? json.colorways : []);
const baseData = await DataStore.getMany([
"customColorways",
"actveColorwayID",
]);
setColorways(colorways || fallbackColorways);
setThirdPartyColorways(thirdPartyColorwaysArr);
setCustomColorways(baseData[0]);
setCurrentColorway(baseData[1]);
}
const cached_loadUI = useCallback(loadUI, [setColorways, setCustomColorways, setCurrentColorway]);
async function searchColorways(e: string) {
if (!e) {
cached_loadUI();
return;
}
const colorwaySourceFiles = await DataStore.get("colorwaySourceFiles");
const data = await Promise.all(
colorwaySourceFiles.map((url: string) =>
fetch(url).then((res) => res.json().then(dt => { return { colorways: dt.colorways, url: res.url }; }).catch(() => { return { colorways: [], url: res.url }; }))
)
);
const colorways = data.flatMap((json) => json.url === defaultColorwaySource ? json.colorways : []);
const thirdPartyColorwaysArr = data.flatMap((json) => json.url !== defaultColorwaySource ? json.colorways : []);
const baseData = await DataStore.get("customColorways");
var results: Colorway[] = [];
(colorways || fallbackColorways).find((Colorway: Colorway) => {
if (Colorway.name.toLowerCase().includes(e.toLowerCase()))
results.push(Colorway);
});
var thirdPartyResults: Colorway[] = [];
(thirdPartyColorwaysArr).find((Colorway: Colorway) => {
if (Colorway.name.toLowerCase().includes(e.toLowerCase()))
thirdPartyResults.push(Colorway);
});
var customResults: Colorway[] = [];
baseData.find((Colorway: Colorway) => {
if (Colorway.name.toLowerCase().includes(e.toLowerCase()))
customResults.push(Colorway);
});
setColorways(results);
setThirdPartyColorways(thirdPartyResults);
setCustomColorways(customResults);
}
useEffect(() => {
if (!searchString) {
cached_loadUI();
}
setLoaderHeight("0px");
}, [searchString]);
function ReloadPopout(onClose: () => void) {
return (
<Menu.Menu
navId="dc-reload-menu"
onClose={onClose}
>
<Menu.MenuItem
id="dc-force-reload"
label="Force Reload"
action={async () => {
setLoaderHeight("2px");
const colorwaySourceFiles = await DataStore.get(
"colorwaySourceFiles"
);
const responses: Response[] = await Promise.all(
colorwaySourceFiles.map((url: string) =>
fetch(url, { cache: "no-store" })
)
);
const data = await Promise.all(
responses.map((res: Response) => {
setLoaderHeight("0px");
return res.json().then(dt => { return { colorways: dt.colorways, url: res.url }; }).catch(() => { return { colorways: [], url: res.url }; });
}
));
const colorways = data.flatMap((json) => json.url === defaultColorwaySource ? json.colorways : []);
const thirdPartyColorwaysArr = data.flatMap((json) => json.url !== defaultColorwaySource ? json.colorways : []);
const baseData = await DataStore.getMany([
"customColorways",
"actveColorwayID",
]);
setColorways(colorways || fallbackColorways);
setThirdPartyColorways(thirdPartyColorwaysArr);
setCustomColorways(baseData[0]);
setCurrentColorway(baseData[1]);
}}
/>
</Menu.Menu>
);
}
return (
<SettingsTab title="Colors">
<div className="colorwaysSettingsSelector-wrapper">
<Flex style={{ gap: "0" }}>
<Select className="colorwaySelector-pill colorwaySelector-pill_select" options={[{
value: "all",
label: "All"
},
{
value: "official",
label: "Official"
},
{
value: "3rdparty",
label: "3rd-Party"
},
{
value: "custom",
label: "Custom"
}]} select={value => {
setVisibility(value);
}} isSelected={value => visibility === value} serialize={String} />
<TextInput
inputClassName="colorwaySelector-searchInput"
className="colorwaySelector-search"
placeholder="Search for Colorways..."
value={searchString}
onChange={(e: string) => [searchColorways, setSearchString].forEach(t => t(e))}
/>
<Tooltip text="Refresh Colorways...">
{({ onMouseEnter, onMouseLeave }) => {
return <Popout
position="bottom"
align="right"
animation={Popout.Animation.NONE}
shouldShow={showReloadMenu}
onRequestClose={() => setShowReloadMenu(false)}
renderPopout={() => ReloadPopout(() => setShowReloadMenu(false))}
>
{(_, { isShown }) => (
<Button
innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON}
color={Button.Colors.PRIMARY}
style={{ marginLeft: "8px" }}
id="colorway-refreshcolorway"
onMouseEnter={isShown ? () => { } : onMouseEnter}
onMouseLeave={isShown ? () => { } : onMouseLeave}
onClick={() => {
setLoaderHeight("2px");
cached_loadUI().then(() => setLoaderHeight("0px"));
}}
onContextMenu={() => { onMouseLeave(); setShowReloadMenu(v => !v); }}
>
<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>
)}
</Popout>;
}}
</Tooltip>
<Tooltip text="Create Colorway...">
{({ onMouseEnter, onMouseLeave }) => <Button
innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON}
color={Button.Colors.PRIMARY}
style={{ marginLeft: "8px" }}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={() => openModal((props) => <CreatorModal
modalProps={props}
loadUIProps={cached_loadUI}
/>)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
role="img"
width="20"
height="20"
style={{ padding: "6px", boxSizing: "content-box" }}
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"
/>
</svg>
</Button>}
</Tooltip>
<Tooltip text="Open Color Stealer">
{({ onMouseEnter, onMouseLeave }) => <Button
innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON}
color={Button.Colors.PRIMARY}
style={{ marginLeft: "8px" }}
id="colorway-opencolorstealer"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={() => openModal((props) => <ColorPickerModal modalProps={props} />)}
>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" style={{ padding: "6px", boxSizing: "content-box" }} fill="currentColor" viewBox="0 0 16 16">
<path d="M12.433 10.07C14.133 10.585 16 11.15 16 8a8 8 0 1 0-8 8c1.996 0 1.826-1.504 1.649-3.08-.124-1.101-.252-2.237.351-2.92.465-.527 1.42-.237 2.433.07zM8 5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm4.5 3a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM5 6.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm.5 6.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z" />
</svg>
</Button>}
</Tooltip>
</Flex>
<div className="colorwaysLoader-barContainer"><div className="colorwaysLoader-bar" style={{ height: loaderHeight }} /></div>
<div className="ColorwaySelectorWrapper">
{visibleColorwayArray.length === 0 &&
<Forms.FormTitle
style={{
marginBottom: 0,
width: "100%",
textAlign: "center",
}}
>
No colorways...
</Forms.FormTitle>
}
{visibleColorwayArray.map((color, ind) => {
var colors: Array<string> = color.colors || [
"accent",
"primary",
"secondary",
"tertiary",
];
return (
<Tooltip text={color.name}>
{({ onMouseEnter, onMouseLeave }) => {
return (
<div
className="discordColorway"
id={"colorway-" + color.name}
data-last-official={
ind + 1 === colorways.length
}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<div
className="colorwayInfoIconContainer"
onClick={() => {
openModal((props) => (
<ColorwayInfoModal
modalProps={
props
}
colorwayProps={
color
}
discrimProps={customColorways.includes(
color
)}
loadUIProps={cached_loadUI}
/>
));
}}
>
<div className="colorwayInfoIcon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
viewBox="0 0 16 16"
>
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" />
</svg>
</div>
</div>
<div className="colorwayCheckIconContainer">
<div className="colorwayCheckIcon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="currentColor"
viewBox="0 0 24 24"
>
<circle r="8" cx="12" cy="12" fill="var(--white-500)" />
<g fill="none" fill-rule="evenodd">
<path fill="var(--brand-500)" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
</g>
</svg>
</div>
</div>
<div
className="discordColorwayPreviewColorContainer"
onClick={async () => {
const [
onDemandWays,
onDemandWaysTintedText,
onDemandWaysDiscordSaturation
] = await DataStore.getMany([
"onDemandWays",
"onDemandWaysTintedText",
"onDemandWaysDiscordSaturation"
]);
if (currentColorway === color.name) {
DataStore.set("actveColorwayID", null);
DataStore.set("actveColorway", null);
ColorwayCSS.remove();
} else {
DataStore.set("activeColorwayColors", color.colors);
DataStore.set("actveColorwayID", color.name);
if (onDemandWays) {
const demandedColorway = generateCss(
colorToHex(color.primary),
colorToHex(color.secondary),
colorToHex(color.tertiary),
colorToHex(color.accent),
onDemandWaysTintedText,
onDemandWaysDiscordSaturation
);
DataStore.set("actveColorway", demandedColorway);
ColorwayCSS.set(demandedColorway);
} else {
DataStore.set("actveColorway", color["dc-import"]);
ColorwayCSS.set(color["dc-import"]);
}
}
setCurrentColorway(await DataStore.get("actveColorwayID") as string);
}}
>
{colors.map((colorItm) => {
return (
<div
className="discordColorwayPreviewColor"
style={{
// prettier-ignore
backgroundColor: color[colorItm],
}}
/>
);
})}
</div>
{currentColorway === color.name && <SelectionCircle />}
</div>
);
}}
</Tooltip>
);
})}
</div>
</div>
</SettingsTab>
);
}

View file

@ -27,7 +27,6 @@ import { versionData } from "userplugins/discordColorways";
import { defaultColorwaySource, fallbackColorways, knownColorwaySources } from "../../constants"; import { defaultColorwaySource, fallbackColorways, knownColorwaySources } from "../../constants";
import { Colorway } from "../../types"; import { Colorway } from "../../types";
import Divider from "../Divider";
import { CloseIcon } from "../Icons"; import { CloseIcon } from "../Icons";
export default function () { export default function () {
@ -165,7 +164,8 @@ export default function () {
&& <Button && <Button
innerClassName="colorwaysSettings-iconButtonInner" innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON} size={Button.Sizes.ICON}
color={Button.Colors.TRANSPARENT} color={Button.Colors.PRIMARY}
look={Button.Looks.OUTLINED}
onClick={async () => { onClick={async () => {
var sourcesArr: string[] = []; var sourcesArr: string[] = [];
const colorwaySourceFilesArr = await DataStore.get("colorwaySourceFiles"); const colorwaySourceFilesArr = await DataStore.get("colorwaySourceFiles");
@ -183,7 +183,8 @@ export default function () {
<Button <Button
innerClassName="colorwaysSettings-iconButtonInner" innerClassName="colorwaysSettings-iconButtonInner"
size={Button.Sizes.ICON} size={Button.Sizes.ICON}
color={Button.Colors.TRANSPARENT} color={Button.Colors.PRIMARY}
look={Button.Looks.OUTLINED}
onClick={() => { Clipboard.copy(colorwaySourceFile); }} onClick={() => { Clipboard.copy(colorwaySourceFile); }}
> >
<CopyIcon width={20} height={20} /> <CopyIcon width={20} height={20} />
@ -191,7 +192,7 @@ export default function () {
</div> </div>
)} )}
</Flex> </Flex>
<Divider /> <Forms.FormDivider style={{ margin: "20px 0" }} />
<Forms.FormTitle tag="h5">Quick Switch</Forms.FormTitle> <Forms.FormTitle tag="h5">Quick Switch</Forms.FormTitle>
<Switch <Switch
value={colorsButtonVisibility} value={colorsButtonVisibility}

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { ModalProps, ModalRoot, openModal } from "@utils/modal";
import { import {
Forms, Forms,
Text, Text,
@ -31,15 +32,137 @@ export default function ({
previewCSS?: string; previewCSS?: string;
}) { }) {
const [collapsed, setCollapsed] = useState<boolean>(isCollapsed); const [collapsed, setCollapsed] = useState<boolean>(isCollapsed);
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(--bg-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(--bg-guild-button, ${primary})` }}
onMouseEnter={e => e.currentTarget.style.background = accent}
onMouseLeave={e => e.currentTarget.style.background = `var(--bg-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(--bg-guild-button, ${primary})` }}
onMouseEnter={e => e.currentTarget.style.background = accent}
onMouseLeave={e => e.currentTarget.style.background = `var(--bg-guild-button, ${primary})`}
/>
</div>
<div className="colorwayPreview-guild">
<div
className="colorwayPreview-guildItem"
style={{ background: `var(--bg-guild-button, ${primary})` }}
onMouseEnter={e => e.currentTarget.style.background = accent}
onMouseLeave={e => e.currentTarget.style.background = `var(--bg-guild-button, ${primary})`}
/>
</div>
</div>
<div className="colorwayPreview-channels" style={{ background: `var(--bg-overlay-3, ${secondary})` }}>
<div
className="colorwayPreview-userArea"
style={{
background: `var(--bg-secondary-alt, hsl(${HexToHSL(secondary)[0]} ${HexToHSL(secondary)[1]}% ${Math.max(HexToHSL(secondary)[2] - 3.6, 0)}%))`
}}
/>
<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}
>
Preview
</Text>
</div>
</div>
<div className="colorwayPreview-chat" style={{ background: `var(--bg-overlay-chat, ${primary})` }}>
<div
className="colorwayPreview-chatBox"
style={{
background: `var(--bg-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>
</div>
);
}
return ( return (
<div <div className={`${collapsed ? "colorwaysPreview colorwaysPreview-collapsed" : "colorwaysPreview"} ${className || ""}`}>
className={
`${collapsed === true
? "colorwaysPreview colorwaysPreview-collapsed"
: "colorwaysPreview"
} ${className}`
}
>
<div <div
className="colorwaysCreator-settingItm colorwaysCreator-settingHeader" className="colorwaysCreator-settingItm colorwaysCreator-settingHeader"
onClick={() => setCollapsed(!collapsed)} onClick={() => setCollapsed(!collapsed)}
@ -80,143 +203,3 @@ export default function ({
</div> </div>
); );
} }
function ThemePreview({
accent,
primary,
secondary,
tertiary,
previewCSS
}: {
accent: string,
primary: string,
secondary: string,
tertiary: string,
previewCSS?: string;
}) {
const [isFullscreen, setIsFullscreen] = useState<boolean>(false);
return (
<div
className="colorwaysPreview-container"
>
<style>
{previewCSS}
</style>
<div
className="colorwaysPreview-wrapper"
style={{ backgroundColor: tertiary }}
>
<div className="colorwaysPreview-titlebar" />
<div className="colorwaysPreview-body">
<div className="colorwayPreview-guilds">
<div className="colorwayPreview-guild">
<div
className="colorwayPreview-guildItem"
style={{ backgroundColor: primary }}
onMouseEnter={e => e.currentTarget.style.backgroundColor = accent}
onMouseLeave={e => e.currentTarget.style.backgroundColor = primary}
onClick={e => {
if (!document.fullscreenElement) {
e.currentTarget
.parentElement
?.parentElement
?.parentElement
?.parentElement
?.requestFullscreen();
} else {
document.exitFullscreen();
}
setIsFullscreen(!isFullscreen);
}}
>
{isFullscreen ? <CloseIcon /> : <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={{ backgroundColor: primary }}
onMouseEnter={e => { e.currentTarget.style.backgroundColor = accent; }}
onMouseLeave={e => { e.currentTarget.style.backgroundColor = primary; }}
/>
</div>
<div className="colorwayPreview-guild">
<div
className="colorwayPreview-guildItem"
style={{ backgroundColor: primary }}
onMouseEnter={e => { e.currentTarget.style.backgroundColor = accent; }}
onMouseLeave={e => { e.currentTarget.style.backgroundColor = primary; }}
/>
</div>
</div>
<div className="colorwayPreview-channels" style={{ backgroundColor: secondary }}>
<div
className="colorwayPreview-userArea"
style={{
backgroundColor: "hsl(" + HexToHSL(secondary)[0] + " " + HexToHSL(secondary)[1] + "% " + Math.max(HexToHSL(secondary)[2] - 3.6, 0) + "%)"
}}
/>
<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}
>
Preview
</Text>
</div>
</div>
<div className="colorwayPreview-chat" style={{ backgroundColor: primary }}>
<div
className="colorwayPreview-chatBox"
style={{
backgroundColor: "hsl(" + HexToHSL(primary)[0] + " " + HexToHSL(primary)[1] + "% " + Math.min(HexToHSL(primary)[2] + 3.6, 100) + "%)"
}}
/>
<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)}%`
} as React.CSSProperties}
/>
</div>
</div>
</div>
</div>
);
}

View file

@ -315,3 +315,10 @@ export const knownThemeVars = {
} }
} }
}; };
export const mainColors = [
{ name: "accent", title: "Accent", var: "--brand-experiment" },
{ name: "primary", title: "Primary", var: "--background-primary" },
{ name: "secondary", title: "Secondary", var: "--background-secondary" },
{ name: "tertiary", title: "Tertiary", var: "--background-tertiary" }
];

View file

@ -6,7 +6,7 @@
import { Plugins } from "Vencord"; import { Plugins } from "Vencord";
import { colorToHex, HexToHSL } from "./utils"; import { HexToHSL } from "./utils";
export const colorVariables: string[] = [ export const colorVariables: string[] = [
"brand-100", "brand-100",
@ -290,9 +290,73 @@ const BrandLightDiffs = {
900: -61.6 900: -61.6
}; };
function gradientBase(accentColor?: string, discordSaturation = false) { export const pureGradientBase = `
return `@import url(//dablulite.github.io/css-snippets/NoLightInDark/import.css); .theme-dark :is(.colorwaysPreview-modal, .colorwaysPreview) {
@import url(//dablulite.github.io/css-snippets/NitroThemesFix/import.css); --bg-overlay-color: 0 0 0;
--bg-overlay-color-inverse: 255 255 255;
--bg-overlay-opacity-1: 0.85;
--bg-overlay-opacity-2: 0.8;
--bg-overlay-opacity-3: 0.7;
--bg-overlay-opacity-4: 0.5;
--bg-overlay-opacity-5: 0.4;
--bg-overlay-opacity-6: 0.1;
--bg-overlay-opacity-hover: 0.5;
--bg-overlay-opacity-hover-inverse: 0.08;
--bg-overlay-opacity-active: 0.45;
--bg-overlay-opacity-active-inverse: 0.1;
--bg-overlay-opacity-selected: 0.4;
--bg-overlay-opacity-selected-inverse: 0.15;
--bg-overlay-opacity-chat: 0.8;
--bg-overlay-opacity-home: 0.85;
--bg-overlay-opacity-home-card: 0.8;
--bg-overlay-opacity-app-frame: var(--bg-overlay-opacity-4);
--bg-guild-button: rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-6));
--bg-secondary-alt: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-3)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-3))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-chat-header: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-2)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-2))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
}
.theme-light :is(.colorwaysPreview-modal, .colorwaysPreview) {
--bg-overlay-color: 255 255 255;
--bg-overlay-color-inverse: 0 0 0;
--bg-overlay-opacity-1: 0.9;
--bg-overlay-opacity-2: 0.8;
--bg-overlay-opacity-3: 0.7;
--bg-overlay-opacity-4: 0.6;
--bg-overlay-opacity-5: 0.3;
--bg-overlay-opacity-6: 0.15;
--bg-overlay-opacity-hover: 0.7;
--bg-overlay-opacity-hover-inverse: 0.02;
--bg-overlay-opacity-active: 0.65;
--bg-overlay-opacity-active-inverse: 0.03;
--bg-overlay-opacity-selected: 0.6;
--bg-overlay-opacity-selected-inverse: 0.04;
--bg-overlay-opacity-chat: 0.9;
--bg-overlay-opacity-home: 0.7;
--bg-overlay-opacity-home-card: 0.9;
--bg-overlay-opacity-app-frame: var(--bg-overlay-opacity-5);
--bg-guild-button: rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-3));
--bg-secondary-alt: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-1)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-1))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-chat-header: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-1)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-1))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
}
.colorwaysPreview-modal,
.colorwaysPreview {
--bg-overlay-1: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-1)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-1))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-2: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-2)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-2))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-3: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-3)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-3))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-4: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-4)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-4))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-5: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-5)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-5))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-6: linear-gradient(rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-6)),rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-6))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-hover: linear-gradient(rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-hover-inverse)),rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-hover-inverse))) fixed 0 0/cover,linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-hover)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-hover))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-active: linear-gradient(rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-active-inverse)),rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-active-inverse))) fixed 0 0/cover,linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-active)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-active))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-selected: linear-gradient(rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-selected-inverse)),rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-selected-inverse))) fixed 0 0/cover,linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-selected)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-selected))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-chat: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-chat)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-chat))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-home: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-home-card: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home-card)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home-card))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
--bg-overlay-app-frame: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-app-frame)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-app-frame))) fixed 0 0/cover,var(--gradient-theme-bg) fixed 0 0/cover;
}`;
export function gradientBase(accentColor?: string, discordSaturation = false) {
return `@import url(//dablulite.github.io/css-snippets/NitroThemesFix/import.css);
.theme-dark { .theme-dark {
--bg-overlay-color: 0 0 0; --bg-overlay-color: 0 0 0;
--bg-overlay-color-inverse: 255 255 255; --bg-overlay-color-inverse: 255 255 255;
@ -401,14 +465,11 @@ function gradientBase(accentColor?: string, discordSaturation = false) {
--bg-overlay-chat: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-chat)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-chat))) fixed 0 0/cover,var(--custom-theme-background) fixed 0 0/cover; --bg-overlay-chat: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-chat)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-chat))) fixed 0 0/cover,var(--custom-theme-background) fixed 0 0/cover;
--bg-overlay-home: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home))) fixed 0 0/cover,var(--custom-theme-background) fixed 0 0/cover; --bg-overlay-home: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home))) fixed 0 0/cover,var(--custom-theme-background) fixed 0 0/cover;
--bg-overlay-home-card: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home-card)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home-card))) fixed 0 0/cover,var(--custom-theme-background) fixed 0 0/cover; --bg-overlay-home-card: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home-card)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-home-card))) fixed 0 0/cover,var(--custom-theme-background) fixed 0 0/cover;
--bg-overlay-app-frame: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-app-frame)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-app-frame))) fixed 0 0/cover,var(--custom-theme-background) fixed 0 0/cover;`; --bg-overlay-app-frame: linear-gradient(rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-app-frame)),rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-app-frame))) fixed 0 0/cover,var(--custom-theme-background) fixed 0 0/cover;
}`;
} }
export function generateCss(primaryColor: string, secondaryColor: string, tertiaryColor: string, accentColor: string, tintedText: boolean, discordSaturation: boolean) { export function generateCss(primaryColor: string, secondaryColor: string, tertiaryColor: string, accentColor: string, tintedText: boolean, discordSaturation: boolean) {
primaryColor = colorToHex(primaryColor);
secondaryColor = colorToHex(secondaryColor);
tertiaryColor = colorToHex(tertiaryColor);
accentColor = colorToHex(accentColor);
const colorwayCss = `/*Automatically Generated - Colorway Creator V${(Plugins.plugins.DiscordColorways as any).creatorVersion}*/ const colorwayCss = `/*Automatically Generated - Colorway Creator V${(Plugins.plugins.DiscordColorways as any).creatorVersion}*/
:root:root { :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-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)};
@ -446,7 +507,9 @@ 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-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-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-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)}%;${tintedText ? `\n --primary-460-hsl: 0 calc(var(--saturation-factor, 1)*0%) 50%; --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)}%);
${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-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-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-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%)")};` : ""}
@ -635,15 +698,22 @@ export function getPreset(primaryColor?: string, secondaryColor?: string, tertia
} }
function gradientType1(discordSaturation = false) { function gradientType1(discordSaturation = false) {
return `${gradientBase(accentColor, discordSaturation)} return {
--custom-theme-background: linear-gradient(239.16deg, #${primaryColor} 10.39%, #${secondaryColor} 26.87%, #${tertiaryColor} 48.31%, hsl(${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + secondaryColor)[1]}%) ${Math.min(HexToHSL("#" + secondaryColor)[2] + 3.6, 100)}%) 64.98%, #${primaryColor} 92.5%); full: `${gradientBase(accentColor, discordSaturation)}
}`; :root:root {
--custom-theme-background: linear-gradient(239.16deg, #${primaryColor} 10.39%, #${secondaryColor} 26.87%, #${tertiaryColor} 48.31%, hsl(${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + secondaryColor)[1]}%) ${Math.min(HexToHSL("#" + secondaryColor)[2] + 3.6, 100)}%) 64.98%, #${primaryColor} 92.5%);
}`,
base: `239.16deg, #${primaryColor} 10.39%, #${secondaryColor} 26.87%, #${tertiaryColor} 48.31%, hsl(${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + secondaryColor)[1]}%) ${Math.min(HexToHSL("#" + secondaryColor)[2] + 3.6, 100)}%) 64.98%, #${primaryColor} 92.5%`
};
} }
function gradientType2(discordSaturation = false) { function gradientType2(discordSaturation = false) {
return `${gradientBase(accentColor, discordSaturation)} return {
--custom-theme-background: linear-gradient(48.17deg, #${primaryColor} 11.21%, #${secondaryColor} 61.92%); full: `${gradientBase(accentColor, discordSaturation)}
}`; :root:root {
--custom-theme-background: linear-gradient(48.17deg, #${primaryColor} 11.21%, #${secondaryColor} 61.92%);
}`, base: `48.17deg, #${primaryColor} 11.21%, #${secondaryColor} 61.92%`
};
} }
function hueRotation(discordSaturation = false) { function hueRotation(discordSaturation = false) {

View file

@ -19,10 +19,9 @@ import {
import ColorPickerModal from "./components/ColorPicker"; import ColorPickerModal from "./components/ColorPicker";
import ColorwaysButton from "./components/ColorwaysButton"; import ColorwaysButton from "./components/ColorwaysButton";
import CreatorModal from "./components/CreatorModal"; import CreatorModal from "./components/CreatorModal";
import SelectorModal from "./components/SelectorModal"; import Selector from "./components/Selector";
import ManageColorwaysPage from "./components/SettingsTabs/ManageColorwaysPage"; import ManageColorwaysPage from "./components/SettingsTabs/ManageColorwaysPage";
import OnDemandWaysPage from "./components/SettingsTabs/OnDemandPage"; import OnDemandWaysPage from "./components/SettingsTabs/OnDemandPage";
import SelectorPage from "./components/SettingsTabs/SelectorPage";
import SettingsPage from "./components/SettingsTabs/SettingsPage"; import SettingsPage from "./components/SettingsTabs/SettingsPage";
import Spinner from "./components/Spinner"; import Spinner from "./components/Spinner";
import { defaultColorwaySource } from "./constants"; import { defaultColorwaySource } from "./constants";
@ -95,8 +94,8 @@ export const ColorwayCSS = {
}; };
export const versionData = { export const versionData = {
pluginVersion: "5.6.2", pluginVersion: "5.6.4.2",
creatorVersion: "1.18.1", creatorVersion: "1.18.3",
}; };
export default definePlugin({ export default definePlugin({
@ -111,7 +110,7 @@ export default definePlugin({
pluginVersion: versionData.pluginVersion, pluginVersion: versionData.pluginVersion,
creatorVersion: versionData.creatorVersion, creatorVersion: versionData.creatorVersion,
toolboxActions: { toolboxActions: {
"Change Colorway": () => openModal(props => <SelectorModal modalProps={props} />), "Change Colorway": () => openModal(props => <Selector modalProps={props} />),
"Open Colorway Creator": () => openModal(props => <CreatorModal modalProps={props} />), "Open Colorway Creator": () => openModal(props => <CreatorModal modalProps={props} />),
"Open Color Stealer": () => openModal(props => <ColorPickerModal modalProps={props} />), "Open Color Stealer": () => openModal(props => <ColorPickerModal modalProps={props} />),
"Open Settings": () => SettingsRouter.open("ColorwaysSettings"), "Open Settings": () => SettingsRouter.open("ColorwaysSettings"),
@ -152,12 +151,12 @@ export default definePlugin({
} }
} }
], ],
set ColorPicker(e) { set ColorPicker(e) {
ColorPicker = e; ColorPicker = e;
}, },
makeSettingsCategories(SectionTypes: Record<string, unknown>) { makeSettingsCategories(SectionTypes: Record<string, unknown>) {
console.log(SectionTypes);
return [ return [
{ {
section: SectionTypes.HEADER, section: SectionTypes.HEADER,
@ -167,7 +166,7 @@ export default definePlugin({
{ {
section: "ColorwaysSelector", section: "ColorwaysSelector",
label: "Colorways", label: "Colorways",
element: SelectorPage, element: () => <Selector isSettings modalProps={{ onClose: () => new Promise(() => true), transitionState: 1 }} />,
className: "dc-colorway-selector" className: "dc-colorway-selector"
}, },
{ {
@ -202,22 +201,20 @@ export default definePlugin({
enableStyle(style); enableStyle(style);
ColorwayCSS.set((await DataStore.get("actveColorway")) || ""); ColorwayCSS.set((await DataStore.get("actveColorway")) || "");
addAccessory("colorways-btn", props => { addAccessory("colorways-btn", props => String(props.message.content).match(/colorway:[0-9a-f]{0,100}/) ? <Button
if (String(props.message.content).match(/colorway:[0-9a-f]{0,71}/)) onClick={() => openModal(modalProps => <CreatorModal
return <Button onClick={() => { modalProps={modalProps}
openModal(propss => ( colorwayID={String(props.message.content).match(/colorway:[0-9a-f]{0,100}/)![0]}
<CreatorModal />)}
modalProps={propss} size={Button.Sizes.SMALL}
colorwayID={String(props.message.content).match(/colorway:[0-9a-f]{0,71}/)![0]} color={Button.Colors.PRIMARY}
/> look={Button.Looks.OUTLINED}
)); >
}} size={Button.Sizes.SMALL} color={Button.Colors.PRIMARY}>Add this Colorway...</Button>; Add this Colorway...
return null; </Button> : null);
});
}, },
stop() { stop() {
removeServerListElement(ServerListRenderPosition.In, this.ColorwaysButton); removeServerListElement(ServerListRenderPosition.In, this.ColorwaysButton);
disableStyle(style); disableStyle(style);
ColorwayCSS.remove(); ColorwayCSS.remove();
removeAccessory("colorways-btn"); removeAccessory("colorways-btn");

View file

@ -47,85 +47,26 @@
border-radius: 50%; border-radius: 50%;
width: 56px; width: 56px;
height: 56px; height: 56px;
box-shadow: 0 0 0 1px var(--interactive-normal); box-shadow: 0 0 0 1.5px var(--interactive-normal);
box-sizing: border-box; box-sizing: border-box;
} }
.discordColorway::before {
content: "";
position: absolute;
top: -2px;
left: -2px;
border-radius: 50%;
width: 64px;
height: 64px;
pointer-events: none;
transition: .15s;
}
.discordColorway.active::before {
box-shadow: inset 0 0 0 2px var(--brand-500), inset 0 0 0 4px var(--background-primary);
}
.discordColorwayPreviewColor { .discordColorwayPreviewColor {
width: calc(50% - 2px); width: 50%;
height: calc(50% - 2px); height: 50%;
}
.discordColorwayPreviewColor:first-child {
margin-top: 2px;
margin-left: 2px;
border-top-left-radius: 52px;
}
.discordColorwayPreviewColor:nth-child(2) {
margin-top: 2px;
margin-right: 2px;
border-top-right-radius: 52px;
}
.discordColorwayPreviewColor:nth-child(3) {
margin-bottom: 2px;
margin-left: 2px;
border-bottom-left-radius: 52px;
}
.discordColorwayPreviewColor:nth-child(4) {
margin-bottom: 2px;
margin-right: 2px;
border-bottom-right-radius: 52px;
} }
.discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(2)))>.discordColorwayPreviewColor { .discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(2)))>.discordColorwayPreviewColor {
height: calc(100% - 4px); height: 100%;
width: calc(100% - 4px); width: 100%;
margin: 2px;
border-radius: 52px;
} }
.discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(3)))>.discordColorwayPreviewColor { .discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(3)))>.discordColorwayPreviewColor {
height: calc(100% - 4px); height: 100%;
margin-top: 2px;
margin-bottom: 2px;
}
.discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(3)))>.discordColorwayPreviewColor:nth-child(1) {
border-top-left-radius: 52px;
border-bottom-left-radius: 52px;
margin-left: 2px;
}
.discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(3)))>.discordColorwayPreviewColor:nth-child(2) {
border-top-right-radius: 52px;
border-bottom-right-radius: 52px;
margin-right: 2px;
} }
.discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(4)))>.discordColorwayPreviewColor:nth-child(3) { .discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(4)))>.discordColorwayPreviewColor:nth-child(3) {
width: calc(100% - 4px); width: 100%;
margin-right: 2px;
border-bottom-right-radius: 52px;
border-bottom-left-radius: 52px;
} }
.ColorwaySelectorWrapper { .ColorwaySelectorWrapper {
@ -138,6 +79,10 @@
scrollbar-width: none !important; scrollbar-width: none !important;
} }
.ColorwaySelectorWrapper::-webkit-scrollbar {
width: 0;
}
.colorwaySelectorModal { .colorwaySelectorModal {
width: 100% !important; width: 100% !important;
min-width: 596px !important; min-width: 596px !important;
@ -163,42 +108,23 @@
width: 72px; width: 72px;
} }
.colorwayCheckIconContainer {
height: 20px;
width: 20px;
background-color: var(--brand-500);
position: absolute;
top: 0;
right: 0;
border-radius: 50%;
opacity: 0;
z-index: +1;
}
.discordColorway.active .colorwayCheckIconContainer {
opacity: 1;
}
.colorwayCheckIcon {
height: 20px;
width: 20px;
color: var(--white-500);
}
.colorwayInfoIconContainer { .colorwayInfoIconContainer {
height: 20px; height: 22px;
width: 20px; width: 22px;
background-color: var(--brand-500); background-color: var(--brand-500);
position: absolute; position: absolute;
top: 0; top: -1px;
left: 0; left: -1px;
border-radius: 50%; border-radius: 50%;
opacity: 0; opacity: 0;
z-index: +1; z-index: +1;
color: var(--white-500);
padding: 1px;
box-sizing: border-box;
} }
.colorwayInfoIconContainer:hover { .colorwayInfoIconContainer:hover {
background-color: var(--brand-700); background-color: var(--brand-experiment-560);
} }
.discordColorway:hover .colorwayInfoIconContainer { .discordColorway:hover .colorwayInfoIconContainer {
@ -206,13 +132,6 @@
transition: .15s; transition: .15s;
} }
.colorwayInfoIcon {
height: 20px;
width: 20px;
color: var(--white-500);
padding: 2px;
}
.colorwayCreator-swatch { .colorwayCreator-swatch {
display: flex; display: flex;
align-items: center; align-items: center;
@ -240,9 +159,7 @@
gap: 8px; gap: 8px;
position: relative; position: relative;
border-radius: 4px; border-radius: 4px;
background-color: var(--background-secondary);
box-sizing: border-box; box-sizing: border-box;
padding: 10px;
} }
.colorwayCreator-colorInput { .colorwayCreator-colorInput {
@ -466,8 +383,8 @@
.colorwaysPreview-modal { .colorwaysPreview-modal {
max-width: unset !important; max-width: unset !important;
max-height: unset !important; max-height: unset !important;
width: fit-content; width: 90vw;
height: fit-content; height: 90vh;
} }
.colorwaysPreview-titlebar { .colorwaysPreview-titlebar {
@ -652,7 +569,6 @@
width: 100%; width: 100%;
height: 32px; height: 32px;
flex: 1 0 auto; flex: 1 0 auto;
transition: background-color .1s linear;
font-family: var(--font-display); font-family: var(--font-display);
font-weight: 500; font-weight: 500;
padding: 12px 16px; padding: 12px 16px;
@ -1001,13 +917,28 @@
box-sizing: border-box; box-sizing: border-box;
min-height: 44px; min-height: 44px;
align-items: center; align-items: center;
background-color: var(--background-secondary); }
.theme-dark .colorwaysSettings-colorwaySource {
background: var(--bg-overlay-3,var(--background-secondary));
}
.theme-light .colorwaysSettings-colorwaySource {
background: var(--bg-overlay-2,var(--background-secondary));
} }
.colorwaysSettings-colorwaySource:hover { .colorwaysSettings-colorwaySource:hover {
background-color: var(--background-secondary-alt); background-color: var(--background-secondary-alt);
} }
.theme-dark .colorwaysSettings-colorwaySource:hover {
background: var(--bg-overlay-1,var(--background-secondary-alt));
}
.theme-light .colorwaysSettings-colorwaySource:hover {
background: var(--bg-overlay-3,var(--background-secondary-alt));
}
.colorwaysSettings-modalRoot { .colorwaysSettings-modalRoot {
min-width: 520px; min-width: 520px;
} }
@ -1204,3 +1135,11 @@
opacity: 0; opacity: 0;
user-select: none; user-select: none;
} }
.dc-warning-card {
padding: 1em;
margin-bottom: 1em;
background-color: var(--info-warning-background);
border-color: var(--info-warning-foreground);
color: var(--info-warning-text);
}

View file

@ -5,6 +5,7 @@
*/ */
export interface Colorway { export interface Colorway {
[key: string]: any,
name: string, name: string,
"dc-import": string, "dc-import": string,
accent: string, accent: string,
@ -17,7 +18,8 @@ export interface Colorway {
colors?: string[], colors?: string[],
isGradient?: boolean, isGradient?: boolean,
sourceUrl?: string, sourceUrl?: string,
sourceName?: string; sourceName?: string,
linearGradient?: string;
} }
export interface ColorPickerProps { export interface ColorPickerProps {