mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-20 20:07:03 -04:00
Fix Timezones & Drop Colorways For Now
This commit is contained in:
parent
9f39648b0f
commit
bef7dc9488
40 changed files with 1 additions and 8923 deletions
|
@ -48,7 +48,6 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend
|
||||||
- CutePats by thororen
|
- CutePats by thororen
|
||||||
- DeadMembers by Kyuuhachi
|
- DeadMembers by Kyuuhachi
|
||||||
- Demonstration by Samwich
|
- Demonstration by Samwich
|
||||||
- DiscordColorways by DaBluLite
|
|
||||||
- DNDWhilePlaying by thororen
|
- DNDWhilePlaying by thororen
|
||||||
- DoNotLeak by Perny
|
- DoNotLeak by Perny
|
||||||
- DontFilterMe by Samwich
|
- DontFilterMe by Samwich
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const ColorwayCSS = {
|
|
||||||
get: () => document.getElementById("activeColorwayCSS")!.textContent || "",
|
|
||||||
set: (e: string) => {
|
|
||||||
if (!document.getElementById("activeColorwayCSS")) {
|
|
||||||
document.head.append(Object.assign(document.createElement("style"), {
|
|
||||||
id: "activeColorwayCSS",
|
|
||||||
textContent: e
|
|
||||||
}));
|
|
||||||
} else (document.getElementById("activeColorwayCSS") as any).textContent = e;
|
|
||||||
},
|
|
||||||
remove: () => document.getElementById("activeColorwayCSS")?.remove(),
|
|
||||||
};
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, useEffect, useState } from "../";
|
|
||||||
import { getAutoPresets } from "../css";
|
|
||||||
import { ModalProps } from "../types";
|
|
||||||
|
|
||||||
export default function ({ modalProps, onChange, autoColorwayId = "" }: { modalProps: ModalProps, onChange: (autoPresetId: string) => void, autoColorwayId: string; }) {
|
|
||||||
const [autoId, setAutoId] = useState(autoColorwayId);
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Auto Preset Settings
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<div className="dc-info-card" style={{ marginTop: "1em" }}>
|
|
||||||
<strong>About the Auto Colorway</strong>
|
|
||||||
<span>The auto colorway allows you to use your system's accent color in combination with a selection of presets that will fully utilize it.</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ marginBottom: "20px" }}>
|
|
||||||
<span className="colorwaysModalSectionHeader">Presets:</span>
|
|
||||||
{Object.values(getAutoPresets()).map(autoPreset => <div
|
|
||||||
className="discordColorway"
|
|
||||||
aria-checked={autoId === autoPreset.id}
|
|
||||||
style={{ padding: "10px", marginBottom: "8px" }}
|
|
||||||
onClick={() => {
|
|
||||||
setAutoId(autoPreset.id);
|
|
||||||
}}>
|
|
||||||
<svg aria-hidden="true" role="img" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
|
||||||
{autoId === autoPreset.id && <circle cx="12" cy="12" r="5" className="radioIconForeground-3wH3aU" fill="currentColor" />}
|
|
||||||
</svg>
|
|
||||||
<span className="colorwayLabel">{autoPreset.name}</span>
|
|
||||||
</div>)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={() => {
|
|
||||||
DataStore.set("activeAutoPreset", autoId);
|
|
||||||
onChange(autoId);
|
|
||||||
modalProps.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
modalProps.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, Toasts, useEffect, useState } from "..";
|
|
||||||
import { mainColors } from "../constants";
|
|
||||||
import { colorVariables } from "../css";
|
|
||||||
import { ModalProps } from "../types";
|
|
||||||
import { getHex } from "../utils";
|
|
||||||
import { CopyIcon } from "./Icons";
|
|
||||||
|
|
||||||
export default function ({ modalProps }: { modalProps: ModalProps; }) {
|
|
||||||
const [ColorVars, setColorVars] = useState<string[]>(colorVariables);
|
|
||||||
const [collapsedSettings, setCollapsedSettings] = useState<boolean>(true);
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
let results: string[];
|
|
||||||
function searchToolboxItems(e: string) {
|
|
||||||
results = [];
|
|
||||||
colorVariables.find((colorVariable: string) => {
|
|
||||||
if (colorVariable.toLowerCase().includes(e.toLowerCase())) {
|
|
||||||
results.push(colorVariable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setColorVars(results);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<div style={{ gap: "8px", marginBottom: "8px", display: "flex" }}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
placeholder="Search for a color:"
|
|
||||||
onChange={({ currentTarget: { value } }) => {
|
|
||||||
searchToolboxItems(value);
|
|
||||||
if (value) {
|
|
||||||
setCollapsedSettings(false);
|
|
||||||
} else {
|
|
||||||
setCollapsedSettings(true);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => setCollapsedSettings(!collapsedSettings)}
|
|
||||||
>
|
|
||||||
<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" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div style={{ color: "var(--text-normal)", overflow: "hidden auto", scrollbarWidth: "none" }} className={collapsedSettings ? " colorwaysColorpicker-collapsed" : ""}>
|
|
||||||
{ColorVars.map((colorVariable: string) => <div
|
|
||||||
id={`colorways-colorstealer-item_${colorVariable}`}
|
|
||||||
className="colorwaysCreator-settingItm colorwaysCreator-toolboxItm"
|
|
||||||
onClick={() => {
|
|
||||||
navigator.clipboard.writeText(getHex(getComputedStyle(document.body).getPropertyValue("--" + colorVariable)));
|
|
||||||
Toasts.show({ message: "Color " + colorVariable + " copied to clipboard", id: "toolbox-color-var-copied", type: 1 });
|
|
||||||
}} style={{ "--brand-experiment": `var(--${colorVariable})` } as React.CSSProperties}>
|
|
||||||
{`Copy ${colorVariable}`}
|
|
||||||
</div>)}
|
|
||||||
</div>
|
|
||||||
<div style={{ justifyContent: "space-between", marginTop: "8px", flexWrap: "wrap", gap: "1em" }} className={collapsedSettings ? "" : " colorwaysColorpicker-collapsed"}>
|
|
||||||
{mainColors.map(mainColor => <div
|
|
||||||
id={`colorways-toolbox_copy-${mainColor.name}`}
|
|
||||||
className="colorwayToolbox-listItem"
|
|
||||||
>
|
|
||||||
<CopyIcon onClick={() => {
|
|
||||||
navigator.clipboard.writeText(getHex(getComputedStyle(document.body).getPropertyValue(mainColor.var)));
|
|
||||||
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>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, useEffect, useState } from "..";
|
|
||||||
import { getPreset } from "../css";
|
|
||||||
import { ModalProps } from "../types";
|
|
||||||
import Setting from "./Setting";
|
|
||||||
import Switch from "./Switch";
|
|
||||||
|
|
||||||
export default function ({ modalProps, onSettings, presetId, hasTintedText, hasDiscordSaturation }: { modalProps: ModalProps, presetId: string, hasTintedText: boolean, hasDiscordSaturation: boolean, onSettings: ({ presetId, tintedText, discordSaturation }: { presetId: string, tintedText: boolean, discordSaturation: boolean; }) => void; }) {
|
|
||||||
const [tintedText, setTintedText] = useState<boolean>(hasTintedText);
|
|
||||||
const [discordSaturation, setDiscordSaturation] = useState<boolean>(hasDiscordSaturation);
|
|
||||||
const [preset, setPreset] = useState<string>(presetId);
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">Creator Settings</h2>
|
|
||||||
<div className="colorwaysModalContent" style={{
|
|
||||||
minWidth: "500px"
|
|
||||||
}}>
|
|
||||||
<span className="colorwaysModalSectionHeader">
|
|
||||||
Presets:
|
|
||||||
</span>
|
|
||||||
<div className="colorwaysScroller" style={{ paddingRight: "2px", marginBottom: "20px", maxHeight: "250px" }}>
|
|
||||||
{Object.values(getPreset()).map(pre => <div
|
|
||||||
aria-checked={preset === pre.id}
|
|
||||||
className="discordColorway"
|
|
||||||
style={{ padding: "10px", marginBottom: "8px" }}
|
|
||||||
onClick={() => {
|
|
||||||
setPreset(pre.id);
|
|
||||||
}}>
|
|
||||||
<svg aria-hidden="true" role="img" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
|
||||||
{preset === pre.id && <circle cx="12" cy="12" r="5" className="radioIconForeground-3wH3aU" fill="currentColor" />}
|
|
||||||
</svg>
|
|
||||||
<span className="colorwayLabel">{pre.name}</span>
|
|
||||||
</div>)}
|
|
||||||
</div>
|
|
||||||
<Setting divider>
|
|
||||||
<Switch value={tintedText} onChange={setTintedText} label="Use colored text" />
|
|
||||||
</Setting>
|
|
||||||
<Switch value={discordSaturation} onChange={setDiscordSaturation} label="Use Discord's saturation" />
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={() => {
|
|
||||||
onSettings({ presetId: preset, discordSaturation: discordSaturation, tintedText: tintedText });
|
|
||||||
modalProps.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
modalProps.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, openModal, Toasts, useEffect, useState } from "..";
|
|
||||||
import { ColorwayCSS } from "../colorwaysAPI";
|
|
||||||
import { generateCss } from "../css";
|
|
||||||
import { colorToHex, hexToString } from "../utils";
|
|
||||||
import CreatorModal from "./CreatorModal";
|
|
||||||
|
|
||||||
export let changeThemeIDCard: (theme: string) => void = () => { };
|
|
||||||
|
|
||||||
export default function ({ props }) {
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
changeThemeIDCard = theme => setTheme(theme);
|
|
||||||
load();
|
|
||||||
return () => {
|
|
||||||
changeThemeIDCard = () => { };
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
if (String(props.message.content).match(/colorway:[0-9a-f]{0,100}/)) {
|
|
||||||
return <div className="colorwayIDCard" data-theme={theme}>
|
|
||||||
{String(props.message.content).match(/colorway:[0-9a-f]{0,100}/g)?.map((colorID: string) => {
|
|
||||||
colorID = hexToString(colorID.split("colorway:")[1]);
|
|
||||||
return <div className="colorwayMessage">
|
|
||||||
<div className="discordColorwayPreviewColorContainer" style={{ width: "56px", height: "56px", marginRight: "16px" }}>
|
|
||||||
{(() => {
|
|
||||||
if (colorID) {
|
|
||||||
if (!colorID.includes(",")) {
|
|
||||||
throw new Error("Invalid Colorway ID");
|
|
||||||
} else {
|
|
||||||
return colorID.split("|").filter(string => string.includes(",#"))[0].split(/,#/).map((color: string) => <div className="discordColorwayPreviewColor" style={{ backgroundColor: `#${colorToHex(color)}` }} />);
|
|
||||||
}
|
|
||||||
} else return null;
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
<div className="colorwayMessage-contents">
|
|
||||||
<span className="colorwaysModalSectionHeader">Colorway{/n:([A-Za-z0-9]+( [A-Za-z0-9]+)+)/i.exec(colorID) ? `: ${/n:([A-Za-z0-9]+( [A-Za-z0-9]+)+)/i.exec(colorID)![1]}` : ""}</span>
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
gap: "1em"
|
|
||||||
}}>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => openModal(modalProps => <CreatorModal
|
|
||||||
modalProps={modalProps}
|
|
||||||
colorwayID={colorID}
|
|
||||||
/>)}
|
|
||||||
>
|
|
||||||
Add this Colorway...
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
navigator.clipboard.writeText(colorID);
|
|
||||||
Toasts.show({
|
|
||||||
message: "Copied Colorway ID Successfully",
|
|
||||||
type: 1,
|
|
||||||
id: "copy-colorway-id-notify",
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy Colorway ID
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
if (!colorID.includes(",")) {
|
|
||||||
throw new Error("Invalid Colorway ID");
|
|
||||||
} else {
|
|
||||||
colorID.split("|").forEach((prop: string) => {
|
|
||||||
if (prop.includes(",#")) {
|
|
||||||
DataStore.set("activeColorwayObject", {
|
|
||||||
id: "Temporary Colorway", css: generateCss(
|
|
||||||
colorToHex(prop.split(/,#/)[1]),
|
|
||||||
colorToHex(prop.split(/,#/)[2]),
|
|
||||||
colorToHex(prop.split(/,#/)[3]),
|
|
||||||
colorToHex(prop.split(/,#/)[0]),
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
32,
|
|
||||||
"Temporary Colorway"
|
|
||||||
), sourceType: "temporary", source: null
|
|
||||||
});
|
|
||||||
ColorwayCSS.set(generateCss(
|
|
||||||
colorToHex(prop.split(/,#/)[1]),
|
|
||||||
colorToHex(prop.split(/,#/)[2]),
|
|
||||||
colorToHex(prop.split(/,#/)[3]),
|
|
||||||
colorToHex(prop.split(/,#/)[0]),
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
32,
|
|
||||||
"Temporary Colorway"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Apply temporarily
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
})}
|
|
||||||
</div>;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, FluxDispatcher, FluxEvents, openModal, useEffect, useState } from "..";
|
|
||||||
import { getAutoPresets } from "../css";
|
|
||||||
import { ColorwayObject } from "../types";
|
|
||||||
import { PalleteIcon } from "./Icons";
|
|
||||||
import Selector from "./MainModal";
|
|
||||||
import Tooltip from "./Tooltip";
|
|
||||||
|
|
||||||
export default function () {
|
|
||||||
const [activeColorway, setActiveColorway] = useState<string>("None");
|
|
||||||
const [visibility, setVisibility] = useState<boolean>(true);
|
|
||||||
const [isThin, setIsThin] = useState<boolean>(false);
|
|
||||||
const [autoPreset, setAutoPreset] = useState<string>("hueRotation");
|
|
||||||
useEffect(() => {
|
|
||||||
(async function () {
|
|
||||||
setVisibility(await DataStore.get("showColorwaysButton") as boolean);
|
|
||||||
setIsThin(await DataStore.get("useThinMenuButton") as boolean);
|
|
||||||
setAutoPreset(await DataStore.get("activeAutoPreset") as string);
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
|
|
||||||
FluxDispatcher.subscribe("COLORWAYS_UPDATE_BUTTON_HEIGHT" as FluxEvents, ({ isTall }) => {
|
|
||||||
setIsThin(isTall);
|
|
||||||
});
|
|
||||||
|
|
||||||
FluxDispatcher.subscribe("COLORWAYS_UPDATE_BUTTON_VISIBILITY" as FluxEvents, ({ isVisible }) => {
|
|
||||||
setVisibility(isVisible);
|
|
||||||
});
|
|
||||||
|
|
||||||
return <Tooltip text={
|
|
||||||
<>
|
|
||||||
{!isThin ? <>
|
|
||||||
<span>Colorways</span>
|
|
||||||
<span style={{ color: "var(--text-muted)", fontWeight: 500, fontSize: 12 }}>{"Active Colorway: " + activeColorway}</span>
|
|
||||||
</> : <span>{"Active Colorway: " + activeColorway}</span>}
|
|
||||||
{activeColorway === "Auto" ? <span style={{ color: "var(--text-muted)", fontWeight: 500, fontSize: 12 }}>{"Auto Preset: " + (getAutoPresets()[autoPreset].name || "None")}</span> : <></>}
|
|
||||||
</>
|
|
||||||
} position="right"
|
|
||||||
>
|
|
||||||
{({ onMouseEnter, onMouseLeave, onClick }) => visibility ? <div className="ColorwaySelectorBtnContainer">
|
|
||||||
<div
|
|
||||||
className={"ColorwaySelectorBtn" + (isThin ? " ColorwaySelectorBtn_thin" : "")}
|
|
||||||
onMouseEnter={async () => {
|
|
||||||
onMouseEnter();
|
|
||||||
setActiveColorway((await DataStore.get("activeColorwayObject") as ColorwayObject).id || "None");
|
|
||||||
setAutoPreset(await DataStore.get("activeAutoPreset") as string);
|
|
||||||
}}
|
|
||||||
onMouseLeave={onMouseLeave}
|
|
||||||
onClick={() => {
|
|
||||||
onClick();
|
|
||||||
openModal((props: any) => <Selector modalProps={props} />);
|
|
||||||
}}
|
|
||||||
>{isThin ? <span style={{ color: "var(--header-primary)", fontWeight: 700, fontSize: 9 }}>Colorways</span> : <PalleteIcon />}</div>
|
|
||||||
</div> : <></>}
|
|
||||||
</Tooltip>;
|
|
||||||
}
|
|
|
@ -1,321 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, useEffect, useState } from "..";
|
|
||||||
import { knownThemeVars } from "../constants";
|
|
||||||
import { ModalProps } from "../types";
|
|
||||||
import { getFontOnBg, getHex } from "../utils";
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
modalProps,
|
|
||||||
onFinished
|
|
||||||
}: {
|
|
||||||
modalProps: ModalProps;
|
|
||||||
onFinished: ({ accent, primary, secondary, tertiary }: { accent: string, primary: string, secondary: string, tertiary: string; }) => void;
|
|
||||||
}) {
|
|
||||||
const [accentColor, setAccentColor] = useState<string>(getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--brand-experiment")
|
|
||||||
));
|
|
||||||
const [primaryColor, setPrimaryColor] = useState<string>(getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-primary")
|
|
||||||
));
|
|
||||||
const [secondaryColor, setSecondaryColor] = useState<string>(getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-secondary")
|
|
||||||
));
|
|
||||||
const [tertiaryColor, setTertiaryColor] = useState<string>(getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-tertiary")
|
|
||||||
));
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Conflicting Colors Found
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<span className="colorwaysConflictingColors-warning">Multiple known themes have been found, select the colors you want to copy from below:</span>
|
|
||||||
<span className="colorwaysModalSectionHeader">Colors to copy:</span>
|
|
||||||
<div className="colorwayCreator-colorPreviews">
|
|
||||||
<div className="colorwayCreator-colorPreview" style={{ backgroundColor: primaryColor, color: getFontOnBg(primaryColor) }} >Primary</div>
|
|
||||||
<div className="colorwayCreator-colorPreview" style={{ backgroundColor: secondaryColor, color: getFontOnBg(secondaryColor) }} >Secondary</div>
|
|
||||||
<div className="colorwayCreator-colorPreview" style={{ backgroundColor: tertiaryColor, color: getFontOnBg(tertiaryColor) }} >Tertiary</div>
|
|
||||||
<div className="colorwayCreator-colorPreview" style={{ backgroundColor: accentColor, color: getFontOnBg(accentColor) }} >Accent</div>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysCreator-settingCat">
|
|
||||||
<div className="colorwaysCreator-settingsList">
|
|
||||||
<div
|
|
||||||
id="colorways-colorstealer-item_Default"
|
|
||||||
className="colorwaysCreator-settingItm colorwaysCreator-colorPreviewItm"
|
|
||||||
>
|
|
||||||
<span className="colorwaysModalSectionHeader">Discord</span>
|
|
||||||
<div className="colorwayCreator-colorPreviews">
|
|
||||||
<div
|
|
||||||
className="colorwayCreator-colorPreview" style={{
|
|
||||||
backgroundColor: getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-primary")
|
|
||||||
),
|
|
||||||
color: getFontOnBg(
|
|
||||||
getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-primary")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
onClick={() => setPrimaryColor(
|
|
||||||
getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-primary")
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
>Primary</div>
|
|
||||||
<div
|
|
||||||
className="colorwayCreator-colorPreview" style={{
|
|
||||||
backgroundColor: getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-secondary")
|
|
||||||
),
|
|
||||||
color: getFontOnBg(
|
|
||||||
getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-secondary")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
onClick={() => setSecondaryColor(
|
|
||||||
getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-secondary")
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
>Secondary</div>
|
|
||||||
<div
|
|
||||||
className="colorwayCreator-colorPreview" style={{
|
|
||||||
backgroundColor: getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-tertiary")
|
|
||||||
),
|
|
||||||
color: getFontOnBg(
|
|
||||||
getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-tertiary")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
onClick={() => setTertiaryColor(
|
|
||||||
getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--background-tertiary")
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
>Tertiary</div>
|
|
||||||
<div
|
|
||||||
className="colorwayCreator-colorPreview" style={{
|
|
||||||
backgroundColor: getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--brand-experiment")
|
|
||||||
),
|
|
||||||
color: getFontOnBg(
|
|
||||||
getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--brand-experiment")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
onClick={() => setAccentColor(
|
|
||||||
getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--brand-experiment")
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
>Accent</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{Object.values(knownThemeVars).map((theme: any, i) => {
|
|
||||||
if (getComputedStyle(document.body).getPropertyValue(theme.variable)) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
id={
|
|
||||||
"colorways-colorstealer-item_" +
|
|
||||||
Object.keys(knownThemeVars)[i]
|
|
||||||
}
|
|
||||||
className="colorwaysCreator-settingItm colorwaysCreator-colorPreviewItm"
|
|
||||||
>
|
|
||||||
<span className="colorwaysModalSectionHeader">{Object.keys(knownThemeVars)[i] + (theme.alt ? " (Main)" : "")}</span>
|
|
||||||
<div className="colorwayCreator-colorPreviews">
|
|
||||||
{theme.primary && getComputedStyle(document.body).getPropertyValue(theme.primary).match(/^\d.*%$/)
|
|
||||||
? <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_primary"
|
|
||||||
style={{
|
|
||||||
backgroundColor: getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.primary)})`),
|
|
||||||
color: getFontOnBg(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.primary)})`))
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setPrimaryColor(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.primary)})`));
|
|
||||||
}}
|
|
||||||
>Primary</div>
|
|
||||||
: (
|
|
||||||
theme.primary
|
|
||||||
? <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_primary"
|
|
||||||
style={{
|
|
||||||
backgroundColor: getHex(getComputedStyle(document.body).getPropertyValue(theme.primary)),
|
|
||||||
color: getFontOnBg(getHex(getComputedStyle(document.body).getPropertyValue(theme.primary)))
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setPrimaryColor(getHex(getComputedStyle(document.body).getPropertyValue(theme.primary)));
|
|
||||||
}}
|
|
||||||
>Primary</div>
|
|
||||||
: (theme.primaryVariables
|
|
||||||
&& <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_primary"
|
|
||||||
style={{ backgroundColor: `hsl(${getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.l)})`, color: getFontOnBg(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.l)})`)) }}
|
|
||||||
onClick={() => {
|
|
||||||
setPrimaryColor(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.primaryVariables.l)})`));
|
|
||||||
}}
|
|
||||||
>Primary</div>))
|
|
||||||
}
|
|
||||||
{theme.secondary && getComputedStyle(document.body).getPropertyValue(theme.secondary).match(/^\d.*%$/)
|
|
||||||
? <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_secondary"
|
|
||||||
style={{
|
|
||||||
backgroundColor: getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.secondary)})`),
|
|
||||||
color: getFontOnBg(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.secondary)})`))
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setSecondaryColor(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.secondary)})`));
|
|
||||||
}}
|
|
||||||
>Secondary</div>
|
|
||||||
: (theme.secondary
|
|
||||||
? <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_secondary"
|
|
||||||
style={{
|
|
||||||
backgroundColor: getHex(getComputedStyle(document.body).getPropertyValue(theme.secondary)),
|
|
||||||
color: getFontOnBg(getHex(getComputedStyle(document.body).getPropertyValue(theme.secondary)))
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setSecondaryColor(getHex(getComputedStyle(document.body).getPropertyValue(theme.secondary)));
|
|
||||||
}}
|
|
||||||
>Secondary</div>
|
|
||||||
: (theme.secondaryVariables
|
|
||||||
&& <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_secondary"
|
|
||||||
style={{ backgroundColor: `hsl(${getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.l)})`, color: getFontOnBg(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.l)})`)) }}
|
|
||||||
onClick={() => {
|
|
||||||
setSecondaryColor(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.secondaryVariables.l)})`));
|
|
||||||
}}
|
|
||||||
>Secondary</div>))
|
|
||||||
}
|
|
||||||
{theme.tertiary && getComputedStyle(document.body).getPropertyValue(theme.tertiary).match(/^\d.*%$/)
|
|
||||||
? <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_tertiary"
|
|
||||||
style={{
|
|
||||||
backgroundColor: getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.tertiary)})`),
|
|
||||||
color: getFontOnBg(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.tertiary)})`))
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setTertiaryColor(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.tertiary)})`));
|
|
||||||
}}
|
|
||||||
>Tertiary</div>
|
|
||||||
: (theme.tertiary
|
|
||||||
? <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_tertiary"
|
|
||||||
style={{
|
|
||||||
backgroundColor: getHex(getComputedStyle(document.body).getPropertyValue(theme.tertiary)),
|
|
||||||
color: getFontOnBg(getHex(getComputedStyle(document.body).getPropertyValue(theme.tertiary)))
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setTertiaryColor(getHex(getComputedStyle(document.body).getPropertyValue(theme.tertiary)));
|
|
||||||
}}
|
|
||||||
>Tertiary</div>
|
|
||||||
: (theme.tertiaryVariables
|
|
||||||
&& <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_tertiary"
|
|
||||||
style={{ backgroundColor: `hsl(${getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.l)})`, color: getFontOnBg(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.l)})`)) }}
|
|
||||||
onClick={() => {
|
|
||||||
setTertiaryColor(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.tertiaryVariables.l)})`));
|
|
||||||
}}
|
|
||||||
>Tertiary</div>))}
|
|
||||||
{theme.accent && getComputedStyle(document.body).getPropertyValue(theme.accent).match(/^\d.*%$/)
|
|
||||||
? <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_accent"
|
|
||||||
style={{
|
|
||||||
backgroundColor: getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.accent)})`),
|
|
||||||
color: getFontOnBg(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.accent)})`))
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setAccentColor(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.accent)})`));
|
|
||||||
}}
|
|
||||||
>Accent</div>
|
|
||||||
: (theme.accent
|
|
||||||
? <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_accent"
|
|
||||||
style={{
|
|
||||||
backgroundColor: getHex(getComputedStyle(document.body).getPropertyValue(theme.accent)),
|
|
||||||
color: getFontOnBg(getHex(getComputedStyle(document.body).getPropertyValue(theme.accent)))
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setAccentColor(getHex(getComputedStyle(document.body).getPropertyValue(theme.accent)));
|
|
||||||
}}
|
|
||||||
>Accent</div>
|
|
||||||
: (theme.accentVariables
|
|
||||||
&& <div
|
|
||||||
className="colorwayCreator-colorPreview colorwayCreator-colorPreview_accent"
|
|
||||||
style={{ backgroundColor: `hsl(${getComputedStyle(document.body).getPropertyValue(theme.accentVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.accentVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.accentVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.accentVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.accentVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.accentVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.accentVariables.l)})`, color: getFontOnBg(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.accentVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.accentVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.accentVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.accentVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.accentVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.accentVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.accentVariables.l)})`)) }}
|
|
||||||
onClick={() => {
|
|
||||||
setAccentColor(getHex(`hsl(${getComputedStyle(document.body).getPropertyValue(theme.accentVariables.h)} ${!getComputedStyle(document.body).getPropertyValue(theme.accentVariables.s).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.accentVariables.s) + "%") : getComputedStyle(document.body).getPropertyValue(theme.accentVariables.s)} ${!getComputedStyle(document.body).getPropertyValue(theme.accentVariables.l).includes("%") ? (getComputedStyle(document.body).getPropertyValue(theme.accentVariables.l) + "%") : getComputedStyle(document.body).getPropertyValue(theme.accentVariables.l)})`));
|
|
||||||
}}
|
|
||||||
>Accent</div>))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={() => {
|
|
||||||
onFinished({
|
|
||||||
accent: accentColor,
|
|
||||||
primary: primaryColor,
|
|
||||||
secondary: secondaryColor,
|
|
||||||
tertiary: tertiaryColor
|
|
||||||
});
|
|
||||||
modalProps.onClose();
|
|
||||||
}}
|
|
||||||
>Finish</button>
|
|
||||||
</div>
|
|
||||||
</div >;
|
|
||||||
}
|
|
|
@ -1,363 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ColorPicker, DataStore, openModal, PluginProps, Slider, useEffect, useReducer, UserStore, useState } from "..";
|
|
||||||
import { knownThemeVars } from "../constants";
|
|
||||||
import { generateCss, getPreset, gradientPresetIds, PrimarySatDiffs, pureGradientBase } from "../css";
|
|
||||||
import { Colorway, ModalProps } from "../types";
|
|
||||||
import { colorToHex, getHex, HexToHSL, hexToString } from "../utils";
|
|
||||||
import { updateRemoteSources } from "../wsClient";
|
|
||||||
import ColorwayCreatorSettingsModal from "./ColorwayCreatorSettingsModal";
|
|
||||||
import ConflictingColorsModal from "./ConflictingColorsModal";
|
|
||||||
import InputColorwayIdModal from "./InputColorwayIdModal";
|
|
||||||
import SaveColorwayModal from "./SaveColorwayModal";
|
|
||||||
import ThemePreviewCategory from "./ThemePreview";
|
|
||||||
export default function ({
|
|
||||||
modalProps,
|
|
||||||
loadUIProps = () => new Promise(() => { }),
|
|
||||||
colorwayID
|
|
||||||
}: {
|
|
||||||
modalProps: ModalProps;
|
|
||||||
loadUIProps?: () => Promise<void>;
|
|
||||||
colorwayID?: string;
|
|
||||||
}) {
|
|
||||||
const [colors, updateColors] = useReducer((colors: {
|
|
||||||
accent: string,
|
|
||||||
primary: string,
|
|
||||||
secondary: string,
|
|
||||||
tertiary: string;
|
|
||||||
}, action: {
|
|
||||||
task: "accent" | "primary" | "secondary" | "tertiary" | "all",
|
|
||||||
color?: string;
|
|
||||||
colorObj?: {
|
|
||||||
accent: string,
|
|
||||||
primary: string,
|
|
||||||
secondary: string,
|
|
||||||
tertiary: string;
|
|
||||||
};
|
|
||||||
}) => {
|
|
||||||
if (action.task === "all") {
|
|
||||||
return { ...action.colorObj } as {
|
|
||||||
accent: string,
|
|
||||||
primary: string,
|
|
||||||
secondary: string,
|
|
||||||
tertiary: string;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return { ...colors, [action.task as "accent" | "primary" | "secondary" | "tertiary"]: action.color } as {
|
|
||||||
accent: string,
|
|
||||||
primary: string,
|
|
||||||
secondary: string,
|
|
||||||
tertiary: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
accent: "5865f2",
|
|
||||||
primary: "313338",
|
|
||||||
secondary: "2b2d31",
|
|
||||||
tertiary: "1e1f22"
|
|
||||||
});
|
|
||||||
const [colorwayName, setColorwayName] = useState<string>("");
|
|
||||||
const [tintedText, setTintedText] = useState<boolean>(true);
|
|
||||||
const [discordSaturation, setDiscordSaturation] = useState<boolean>(true);
|
|
||||||
const [preset, setPreset] = useState<string>("default");
|
|
||||||
const [presetColorArray, setPresetColorArray] = useState<string[]>(["accent", "primary", "secondary", "tertiary"]);
|
|
||||||
const [mutedTextBrightness, setMutedTextBrightness] = useState<number>(Math.min(HexToHSL("#" + colors.primary)[2] + (3.6 * 3), 100));
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const setColor = [
|
|
||||||
"accent",
|
|
||||||
"primary",
|
|
||||||
"secondary",
|
|
||||||
"tertiary"
|
|
||||||
] as ("accent" | "primary" | "secondary" | "tertiary")[];
|
|
||||||
|
|
||||||
const colorProps = [
|
|
||||||
{
|
|
||||||
name: "Accent",
|
|
||||||
id: "accent"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Primary",
|
|
||||||
id: "primary"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Secondary",
|
|
||||||
id: "secondary"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Tertiary",
|
|
||||||
id: "tertiary"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (colorwayID) {
|
|
||||||
if (!colorwayID.includes(",")) {
|
|
||||||
throw new Error("Invalid Colorway ID");
|
|
||||||
} else {
|
|
||||||
colorwayID.split("|").forEach((prop: string) => {
|
|
||||||
if (prop.includes(",#")) {
|
|
||||||
prop.split(/,#/).forEach((color: string, i: number) => updateColors({ task: setColor[i], color: colorToHex(color) }));
|
|
||||||
}
|
|
||||||
if (prop.includes("n:")) {
|
|
||||||
setColorwayName(prop.split("n:")[1]);
|
|
||||||
}
|
|
||||||
if (prop.includes("p:")) {
|
|
||||||
if (Object.values(getPreset()).map(preset => preset.id).includes(prop.split("p:")[1])) {
|
|
||||||
setPreset(prop.split("p:")[1]);
|
|
||||||
setPresetColorArray(getPreset()[prop.split("p:")[1]].colors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const colorPickerProps = {
|
|
||||||
suggestedColors: [
|
|
||||||
"#313338",
|
|
||||||
"#2b2d31",
|
|
||||||
"#1e1f22",
|
|
||||||
"#5865f2",
|
|
||||||
],
|
|
||||||
showEyeDropper: true
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">Create a Colorway</h2>
|
|
||||||
<div className="colorwaysModalContent" style={{ minWidth: 500 }}>
|
|
||||||
<span className="colorwaysModalSectionHeader">Name:</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
placeholder="Give your Colorway a name"
|
|
||||||
value={colorwayName}
|
|
||||||
onInput={e => setColorwayName(e.currentTarget.value)}
|
|
||||||
/>
|
|
||||||
<div className="colorwaysCreator-settingCat">
|
|
||||||
<span className="colorwaysModalSectionHeader">Colors & Values:</span>
|
|
||||||
<div className="colorwayCreator-colorPreviews">
|
|
||||||
{colorProps.filter(color => presetColorArray.includes(color.id) || Object.keys(getPreset()[preset].calculated! || {}).includes(color.id)).map(presetColor => {
|
|
||||||
return <ColorPicker
|
|
||||||
label={<span className="colorwaysPicker-colorLabel">{Object.keys(getPreset()[preset].calculated! || {}).includes(presetColor.id) ? (presetColor.name + " (Calculated)") : presetColor.name}</span>}
|
|
||||||
color={!Object.keys(
|
|
||||||
getPreset()[preset].calculated! || {}
|
|
||||||
).includes(presetColor.id) ?
|
|
||||||
parseInt(colors[presetColor.id], 16) :
|
|
||||||
parseInt(
|
|
||||||
colorToHex(
|
|
||||||
getPreset(
|
|
||||||
colors.primary,
|
|
||||||
colors.secondary,
|
|
||||||
colors.tertiary,
|
|
||||||
colors.accent
|
|
||||||
)[preset].calculated![presetColor.id]
|
|
||||||
),
|
|
||||||
16
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onChange={(color: number) => {
|
|
||||||
if (!Object.keys(getPreset()[preset].calculated! || {}).includes(presetColor.id)) {
|
|
||||||
let hexColor = color.toString(16);
|
|
||||||
while (hexColor.length < 6) {
|
|
||||||
hexColor = "0" + hexColor;
|
|
||||||
}
|
|
||||||
updateColors({ task: presetColor.id as "accent" | "primary" | "secondary" | "tertiary", color: hexColor });
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
{...colorPickerProps}
|
|
||||||
/>;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysSettingsDivider" style={{ margin: "10px 0" }} />
|
|
||||||
<span className="colorwaysModalSectionHeader">Muted Text Brightness:</span>
|
|
||||||
<Slider
|
|
||||||
minValue={0}
|
|
||||||
maxValue={100}
|
|
||||||
initialValue={mutedTextBrightness}
|
|
||||||
onValueChange={setMutedTextBrightness}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="colorwaysCreator-setting"
|
|
||||||
onClick={() => openModal((props: ModalProps) => <ColorwayCreatorSettingsModal
|
|
||||||
modalProps={props}
|
|
||||||
hasDiscordSaturation={discordSaturation}
|
|
||||||
hasTintedText={tintedText}
|
|
||||||
presetId={preset}
|
|
||||||
onSettings={({ presetId, tintedText, discordSaturation }) => {
|
|
||||||
setPreset(presetId);
|
|
||||||
setPresetColorArray(getPreset()[presetId].colors);
|
|
||||||
setDiscordSaturation(discordSaturation);
|
|
||||||
setTintedText(tintedText);
|
|
||||||
}} />)}>
|
|
||||||
<span className="colorwaysModalSectionHeader">Settings & Presets</span>
|
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" role="img" style={{ rotate: "-90deg" }}>
|
|
||||||
<path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M7 10L12 15 17 10" aria-hidden="true" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<ThemePreviewCategory
|
|
||||||
accent={"#" + colors.accent}
|
|
||||||
primary={"#" + colors.primary}
|
|
||||||
secondary={"#" + colors.secondary}
|
|
||||||
tertiary={"#" + colors.tertiary}
|
|
||||||
previewCSS={gradientPresetIds.includes(getPreset()[preset].id) ? pureGradientBase + `.colorwaysPreview-modal,.colorwaysPreview-wrapper {--gradient-theme-bg: linear-gradient(${(getPreset(
|
|
||||||
colors.primary,
|
|
||||||
colors.secondary,
|
|
||||||
colors.tertiary,
|
|
||||||
colors.accent
|
|
||||||
)[preset].preset(discordSaturation) as { full: string, base: string; }).base})}` : (tintedText ? `.colorwaysPreview-modal,.colorwaysPreview-wrapper {
|
|
||||||
--primary-500: hsl(${HexToHSL("#" + colors.primary)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + colors.primary)[1] / 100) * (100 + PrimarySatDiffs[500])) * 10) / 10 : HexToHSL("#" + colors.primary)[1]}%) ${mutedTextBrightness || Math.min(HexToHSL("#" + colors.primary)[2] + (3.6 * 3), 100)}%);
|
|
||||||
--primary-360: hsl(${HexToHSL("#" + colors.secondary)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + colors.primary)[1] / 100) * (100 + PrimarySatDiffs[360])) * 10) / 10 : HexToHSL("#" + colors.primary)[1]}%) 90%);
|
|
||||||
}` : "")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
var customColorwayCSS: string = "";
|
|
||||||
if (preset === "default") {
|
|
||||||
customColorwayCSS = generateCss(
|
|
||||||
colors.primary,
|
|
||||||
colors.secondary,
|
|
||||||
colors.tertiary,
|
|
||||||
colors.accent,
|
|
||||||
tintedText,
|
|
||||||
discordSaturation,
|
|
||||||
mutedTextBrightness,
|
|
||||||
(colorwayName || "Colorway")
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
gradientPresetIds.includes(getPreset()[preset].id) ?
|
|
||||||
customColorwayCSS = `/**
|
|
||||||
* @name ${colorwayName || "Colorway"}
|
|
||||||
* @version ${PluginProps.creatorVersion}
|
|
||||||
* @description Automatically generated Colorway.
|
|
||||||
* @author ${UserStore.getCurrentUser().username}
|
|
||||||
* @authorId ${UserStore.getCurrentUser().id}
|
|
||||||
* @preset Gradient
|
|
||||||
*/
|
|
||||||
${(getPreset(colors.primary, colors.secondary, colors.tertiary, colors.accent)[preset].preset(discordSaturation) as { full: string; }).full}` : customColorwayCSS = `/**
|
|
||||||
* @name ${colorwayName || "Colorway"}
|
|
||||||
* @version ${PluginProps.creatorVersion}
|
|
||||||
* @description Automatically generated Colorway.
|
|
||||||
* @author ${UserStore.getCurrentUser().username}
|
|
||||||
* @authorId ${UserStore.getCurrentUser().id}
|
|
||||||
* @preset ${getPreset()[preset].name}
|
|
||||||
*/
|
|
||||||
${(getPreset(colors.primary, colors.secondary, colors.tertiary, colors.accent)[preset].preset(discordSaturation) as string)}`;
|
|
||||||
}
|
|
||||||
const customColorway: Colorway = {
|
|
||||||
name: (colorwayName || "Colorway"),
|
|
||||||
"dc-import": customColorwayCSS,
|
|
||||||
accent: "#" + colors.accent,
|
|
||||||
primary: "#" + colors.primary,
|
|
||||||
secondary: "#" + colors.secondary,
|
|
||||||
tertiary: "#" + colors.tertiary,
|
|
||||||
colors: presetColorArray,
|
|
||||||
author: UserStore.getCurrentUser().username,
|
|
||||||
authorID: UserStore.getCurrentUser().id,
|
|
||||||
isGradient: gradientPresetIds.includes(getPreset()[preset].id),
|
|
||||||
linearGradient: gradientPresetIds.includes(getPreset()[preset].id) ? (getPreset(
|
|
||||||
colors.primary,
|
|
||||||
colors.secondary,
|
|
||||||
colors.tertiary,
|
|
||||||
colors.accent
|
|
||||||
)[preset].preset(discordSaturation) as { base: string; }).base : "",
|
|
||||||
preset: getPreset()[preset].id,
|
|
||||||
creatorVersion: PluginProps.creatorVersion
|
|
||||||
};
|
|
||||||
openModal(props => <SaveColorwayModal modalProps={props} colorways={[customColorway]} onFinish={() => {
|
|
||||||
modalProps.onClose();
|
|
||||||
loadUIProps();
|
|
||||||
updateRemoteSources();
|
|
||||||
}} />);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
function setAllColors({ accent, primary, secondary, tertiary }: { accent: string, primary: string, secondary: string, tertiary: string; }) {
|
|
||||||
updateColors({
|
|
||||||
task: "all",
|
|
||||||
colorObj: {
|
|
||||||
accent: accent.split("#")[1],
|
|
||||||
primary: primary.split("#")[1],
|
|
||||||
secondary: secondary.split("#")[1],
|
|
||||||
tertiary: tertiary.split("#")[1]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var copiedThemes = ["Discord"];
|
|
||||||
Object.values(knownThemeVars).map((theme: { variable: string; variableType?: string; }, i: number) => {
|
|
||||||
if (getComputedStyle(document.body).getPropertyValue(theme.variable)) {
|
|
||||||
copiedThemes.push(Object.keys(knownThemeVars)[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (copiedThemes.length > 1) {
|
|
||||||
openModal(props => <ConflictingColorsModal modalProps={props} onFinished={setAllColors} />);
|
|
||||||
} else {
|
|
||||||
updateColors({
|
|
||||||
task: "all", colorObj: {
|
|
||||||
primary: getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--primary-600")
|
|
||||||
).split("#")[1],
|
|
||||||
secondary: getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--primary-630")
|
|
||||||
).split("#")[1],
|
|
||||||
tertiary: getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--primary-700")
|
|
||||||
).split("#")[1],
|
|
||||||
accent: getHex(
|
|
||||||
getComputedStyle(
|
|
||||||
document.body
|
|
||||||
).getPropertyValue("--brand-experiment")
|
|
||||||
).split("#")[1]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy Current Colors
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => openModal((props: any) => <InputColorwayIdModal modalProps={props} onColorwayId={colorwayID => {
|
|
||||||
hexToString(colorwayID).split(/,#/).forEach((color: string, i: number) => updateColors({ task: setColor[i], color: colorToHex(color) }));
|
|
||||||
}} />)}
|
|
||||||
>
|
|
||||||
Enter Colorway ID
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
modalProps.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { MouseEvent } from "react";
|
|
||||||
|
|
||||||
import { useEffect, useState } from "..";
|
|
||||||
import { SortOptions } from "../types";
|
|
||||||
import { SortIcon } from "./Icons";
|
|
||||||
|
|
||||||
export default function ({ sort, onSortChange }: { sort: SortOptions, onSortChange: (newSort: SortOptions) => void; }) {
|
|
||||||
const [pos, setPos] = useState({ x: 0, y: 0 });
|
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
|
||||||
|
|
||||||
function rightClickContextMenu(e: MouseEvent<HTMLButtonElement, MouseEvent>) {
|
|
||||||
e.stopPropagation();
|
|
||||||
window.dispatchEvent(new Event("click"));
|
|
||||||
setShowMenu(!showMenu);
|
|
||||||
setPos({
|
|
||||||
x: e.currentTarget.getBoundingClientRect().x,
|
|
||||||
y: e.currentTarget.getBoundingClientRect().y + e.currentTarget.offsetHeight + 8
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function onPageClick(this: Window, e: globalThis.MouseEvent) {
|
|
||||||
setShowMenu(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener("click", onPageClick);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("click", onPageClick);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function onSortChange_internal(newSort: SortOptions) {
|
|
||||||
onSortChange(newSort);
|
|
||||||
setShowMenu(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>
|
|
||||||
{showMenu ? <nav className="colorwaysContextMenu" style={{
|
|
||||||
position: "fixed",
|
|
||||||
top: `${pos.y}px`,
|
|
||||||
left: `${pos.x}px`
|
|
||||||
}}>
|
|
||||||
<button onClick={() => onSortChange_internal(1)} className="colorwaysContextMenuItm">
|
|
||||||
Name (A-Z)
|
|
||||||
<svg aria-hidden="true" role="img" width="18" height="18" viewBox="0 0 24 24" style={{
|
|
||||||
marginLeft: "8px"
|
|
||||||
}}>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
|
||||||
{sort === 1 ? <circle className="colorwaysRadioSelected" cx="12" cy="12" r="5" /> : null}
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button onClick={() => onSortChange_internal(2)} className="colorwaysContextMenuItm">
|
|
||||||
Name (Z-A)
|
|
||||||
<svg aria-hidden="true" role="img" width="18" height="18" viewBox="0 0 24 24" style={{
|
|
||||||
marginLeft: "8px"
|
|
||||||
}}>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
|
||||||
{sort === 2 ? <circle className="colorwaysRadioSelected" cx="12" cy="12" r="5" /> : null}
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button onClick={() => onSortChange_internal(3)} className="colorwaysContextMenuItm">
|
|
||||||
Source (A-Z)
|
|
||||||
<svg aria-hidden="true" role="img" width="18" height="18" viewBox="0 0 24 24" style={{
|
|
||||||
marginLeft: "8px"
|
|
||||||
}}>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
|
||||||
{sort === 3 ? <circle className="colorwaysRadioSelected" cx="12" cy="12" r="5" /> : null}
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button onClick={() => onSortChange_internal(4)} className="colorwaysContextMenuItm">
|
|
||||||
Source (Z-A)
|
|
||||||
<svg aria-hidden="true" role="img" width="18" height="18" viewBox="0 0 24 24" style={{
|
|
||||||
marginLeft: "8px"
|
|
||||||
}}>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
|
||||||
{sort === 4 ? <circle className="colorwaysRadioSelected" cx="12" cy="12" r="5" /> : null}
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</nav> : null}
|
|
||||||
<button className="colorwaysPillButton" onClick={() => rightClickContextMenu}><SortIcon width={14} height={14} /> Sort By...</button>
|
|
||||||
</>;
|
|
||||||
}
|
|
|
@ -1,549 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { PropsWithChildren } from "react";
|
|
||||||
|
|
||||||
import { classes } from "../utils";
|
|
||||||
|
|
||||||
interface BaseIconProps extends IconProps {
|
|
||||||
viewBox: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type IconProps = JSX.IntrinsicElements["svg"];
|
|
||||||
|
|
||||||
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
className={classes(className, "dc-icon")}
|
|
||||||
role="img"
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
viewBox={viewBox}
|
|
||||||
{...svgProps}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discord's link icon, as seen in the Message context menu "Copy Message Link" option
|
|
||||||
*/
|
|
||||||
export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
height={height}
|
|
||||||
width={width}
|
|
||||||
className={classes(className, "dc-link-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<g fill="none" fill-rule="evenodd">
|
|
||||||
<path fill="currentColor" d="M10.59 13.41c.41.39.41 1.03 0 1.42-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0 5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.982 2.982 0 0 0 0-4.24 2.982 2.982 0 0 0-4.24 0l-3.53 3.53a2.982 2.982 0 0 0 0 4.24zm2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0 5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.982 2.982 0 0 0 0 4.24 2.982 2.982 0 0 0 4.24 0l3.53-3.53a2.982 2.982 0 0 0 0-4.24.973.973 0 0 1 0-1.42z" />
|
|
||||||
<rect width={width} height={height} />
|
|
||||||
</g>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discord's copy icon, as seen in the user popout right of the username when clicking
|
|
||||||
* your own username in the bottom left user panel
|
|
||||||
*/
|
|
||||||
export function CopyIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-copy-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<g fill="currentColor">
|
|
||||||
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1z" />
|
|
||||||
<path d="M15 5H8c-1.1 0-1.99.9-1.99 2L6 21c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V11l-6-6zM8 21V7h6v5h5v9H8z" />
|
|
||||||
</g>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discord's open external icon, as seen in the user profile connections
|
|
||||||
*/
|
|
||||||
export function OpenExternalIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-open-external-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<polygon
|
|
||||||
fill="currentColor"
|
|
||||||
fillRule="nonzero"
|
|
||||||
points="13 20 11 20 11 8 5.5 13.5 4.08 12.08 12 4.16 19.92 12.08 18.5 13.5 13 8"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ImageIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-image-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path fill="currentColor" d="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function InfoIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-info-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
transform="translate(2 2)"
|
|
||||||
d="M9,7 L11,7 L11,5 L9,5 L9,7 Z M10,18 C5.59,18 2,14.41 2,10 C2,5.59 5.59,2 10,2 C14.41,2 18,5.59 18,10 C18,14.41 14.41,18 10,18 L10,18 Z M10,4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16,4.4771525 0,10 C-1.33226763e-15,12.6521649 1.0535684,15.195704 2.92893219,17.0710678 C4.80429597,18.9464316 7.3478351,20 10,20 C12.6521649,20 15.195704,18.9464316 17.0710678,17.0710678 C18.9464316,15.195704 20,12.6521649 20,10 C20,7.3478351 18.9464316,4.80429597 17.0710678,2.92893219 C15.195704,1.0535684 12.6521649,2.22044605e-16 10,0 L10,4.4408921e-16 Z M9,15 L11,15 L11,9 L9,9 L9,15 L9,15 Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discord's screenshare icon, as seen in the connection panel
|
|
||||||
*/
|
|
||||||
export function ScreenshareIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-screenshare-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M2 4.5C2 3.397 2.897 2.5 4 2.5H20C21.103 2.5 22 3.397 22 4.5V15.5C22 16.604 21.103 17.5 20 17.5H13V19.5H17V21.5H7V19.5H11V17.5H4C2.897 17.5 2 16.604 2 15.5V4.5ZM13.2 14.3375V11.6C9.864 11.6 7.668 12.6625 6 15C6.672 11.6625 8.532 8.3375 13.2 7.6625V5L18 9.6625L13.2 14.3375Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ImageVisible(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-image-visible")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path fill="currentColor" d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h14q.825 0 1.413.587Q21 4.175 21 5v14q0 .825-.587 1.413Q19.825 21 19 21Zm0-2h14V5H5v14Zm1-2h12l-3.75-5-3 4L9 13Zm-1 2V5v14Z" />
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ImageInvisible(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-image-invisible")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path fill="currentColor" d="m21 18.15-2-2V5H7.85l-2-2H19q.825 0 1.413.587Q21 4.175 21 5Zm-1.2 4.45L18.2 21H5q-.825 0-1.413-.587Q3 19.825 3 19V5.8L1.4 4.2l1.4-1.4 18.4 18.4ZM6 17l3-4 2.25 3 .825-1.1L5 7.825V19h11.175l-2-2Zm7.425-6.425ZM10.6 13.4Z" />
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Microphone(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-microphone")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path fillRule="evenodd" clipRule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V21H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1ZM12 4C11.2 4 11 4.66667 11 5V11C11 11.3333 11.2 12 12 12C12.8 12 13 11.3333 13 11V5C13 4.66667 12.8 4 12 4Z" fill="currentColor" />
|
|
||||||
<path fillRule="evenodd" clipRule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V22H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1Z" fill="currentColor" />
|
|
||||||
</Icon >
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CogWheel(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-cog-wheel")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
clipRule="evenodd"
|
|
||||||
fill="currentColor"
|
|
||||||
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"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ReplyIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-reply-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M10 8.26667V4L3 11.4667L10 18.9333V14.56C15 14.56 18.5 16.2667 21 20C20 14.6667 17 9.33333 10 8.26667Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DeleteIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-delete-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M15 3.999V2H9V3.999H3V5.999H21V3.999H15Z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SearchIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-search-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M21.707 20.293L16.314 14.9C17.403 13.504 18 11.799 18 10C18 7.863 17.167 5.854 15.656 4.344C14.146 2.832 12.137 2 10 2C7.863 2 5.854 2.832 4.344 4.344C2.833 5.854 2 7.863 2 10C2 12.137 2.833 14.146 4.344 15.656C5.854 17.168 7.863 18 10 18C11.799 18 13.504 17.404 14.9 16.314L20.293 21.706L21.707 20.293ZM10 16C8.397 16 6.891 15.376 5.758 14.243C4.624 13.11 4 11.603 4 10C4 8.398 4.624 6.891 5.758 5.758C6.891 4.624 8.397 4 10 4C11.603 4 13.109 4.624 14.242 5.758C15.376 6.891 16 8.398 16 10C16 11.603 15.376 13.11 14.242 14.243C13.109 15.376 11.603 16 10 16Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export function PlusIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-plus-icon")}
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
>
|
|
||||||
<polygon
|
|
||||||
fill-rule="nonzero"
|
|
||||||
fill="currentColor"
|
|
||||||
points="15 10 10 10 10 15 8 15 8 10 3 10 3 8 8 8 8 3 10 3 10 8 15 8"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CloseIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-close-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SwatchIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-swatch-icon")}
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
style={{ padding: "4px" }}
|
|
||||||
>
|
|
||||||
<path fill="currentColor" d="M0 .5A.5.5 0 0 1 .5 0h5a.5.5 0 0 1 .5.5v5.277l4.147-4.131a.5.5 0 0 1 .707 0l3.535 3.536a.5.5 0 0 1 0 .708L10.261 10H15.5a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5H3a2.99 2.99 0 0 1-2.121-.879A2.99 2.99 0 0 1 0 13.044m6-.21 7.328-7.3-2.829-2.828L6 7.188v5.647zM4.5 13a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0zM15 15v-4H9.258l-4.015 4H15zM0 .5v12.495V.5z" />
|
|
||||||
<path fill="currentColor" d="M0 12.995V13a3.07 3.07 0 0 0 0-.005z" />
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PalleteIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-pallete-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M 12,0 C 5.3733333,0 0,5.3733333 0,12 c 0,6.626667 5.3733333,12 12,12 1.106667,0 2,-0.893333 2,-2 0,-0.52 -0.2,-0.986667 -0.52,-1.346667 -0.306667,-0.346666 -0.506667,-0.813333 -0.506667,-1.32 0,-1.106666 0.893334,-2 2,-2 h 2.36 C 21.013333,17.333333 24,14.346667 24,10.666667 24,4.7733333 18.626667,0 12,0 Z M 4.6666667,12 c -1.1066667,0 -2,-0.893333 -2,-2 0,-1.1066667 0.8933333,-2 2,-2 1.1066666,0 2,0.8933333 2,2 0,1.106667 -0.8933334,2 -2,2 z M 8.666667,6.6666667 c -1.106667,0 -2.0000003,-0.8933334 -2.0000003,-2 0,-1.1066667 0.8933333,-2 2.0000003,-2 1.106666,0 2,0.8933333 2,2 0,1.1066666 -0.893334,2 -2,2 z m 6.666666,0 c -1.106666,0 -2,-0.8933334 -2,-2 0,-1.1066667 0.893334,-2 2,-2 1.106667,0 2,0.8933333 2,2 0,1.1066666 -0.893333,2 -2,2 z m 4,5.3333333 c -1.106666,0 -2,-0.893333 -2,-2 0,-1.1066667 0.893334,-2 2,-2 1.106667,0 2,0.8933333 2,2 0,1.106667 -0.893333,2 -2,2 z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NoEntrySignIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-no-entry-sign-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M0 0h24v24H0z"
|
|
||||||
fill="none"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function DownloadIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-download-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M12 2a1 1 0 0 1 1 1v10.59l3.3-3.3a1 1 0 1 1 1.4 1.42l-5 5a1 1 0 0 1-1.4 0l-5-5a1 1 0 1 1 1.4-1.42l3.3 3.3V3a1 1 0 0 1 1-1ZM3 20a1 1 0 1 0 0 2h18a1 1 0 1 0 0-2H3Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SafetyIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-safety-icon")}
|
|
||||||
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M12 2a1 1 0 0 1 1 1v10.59l3.3-3.3a1 1 0 1 1 1.4 1.42l-5 5a1 1 0 0 1-1.4 0l-5-5a1 1 0 1 1 1.4-1.42l3.3 3.3V3a1 1 0 0 1 1-1ZM3 20a1 1 0 1 0 0 2h18a1 1 0 1 0 0-2H3Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ImportIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-import-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M.9 3a.9.9 0 0 1 .892.778l.008.123v16.201a.9.9 0 0 1-1.792.121L0 20.102V3.899A.9.9 0 0 1 .9 3Zm14.954 2.26.1-.112a1.2 1.2 0 0 1 1.584-.1l.113.1 5.998 5.998a1.2 1.2 0 0 1 .1 1.584l-.1.112-5.997 6.006a1.2 1.2 0 0 1-1.799-1.584l.1-.113 3.947-3.954H4.8a1.2 1.2 0 0 1-1.191-1.06l-.008-.14a1.2 1.2 0 0 1 1.06-1.192l.14-.008h15.103l-3.95-3.952a1.2 1.2 0 0 1-.1-1.585l.1-.112z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function IDIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
className={classes(props.className, "dc-id-icon")}
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
>
|
|
||||||
<path fill="currentColor" d="M15.3 14.48c-.46.45-1.08.67-1.86.67h-1.39V9.2h1.39c.78 0 1.4.22 1.86.67.46.45.68 1.22.68 2.31 0 1.1-.22 1.86-.68 2.31Z" />
|
|
||||||
<path fill="currentColor" fill-rule="evenodd" d="M5 2a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3H5Zm1 15h2.04V7.34H6V17Zm4-9.66V17h3.44c1.46 0 2.6-.42 3.38-1.25.8-.83 1.2-2.02 1.2-3.58s-.4-2.75-1.2-3.58c-.79-.83-1.92-1.25-3.38-1.25H10Z" clip-rule="evenodd" />
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NotesIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-notes-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M15.3 14.48c-.46.45-1.08.67-1.86.67h-1.39V9.2h1.39c.78 0 1.4.22 1.86.67.46.45.68 1.22.68 2.31 0 1.1-.22 1.86-.68 2.31Z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M5 2a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3H5Zm1 15h2.04V7.34H6V17Zm4-9.66V17h3.44c1.46 0 2.6-.42 3.38-1.25.8-.83 1.2-2.02 1.2-3.58s-.4-2.75-1.2-3.58c-.79-.83-1.92-1.25-3.38-1.25H10Z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CodeIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-code-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M9.6 7.8 4 12l5.6 4.2a1 1 0 0 1 .4.8v1.98c0 .21-.24.33-.4.2l-8.1-6.4a1 1 0 0 1 0-1.56l8.1-6.4c.16-.13.4-.01.4.2V7a1 1 0 0 1-.4.8ZM14.4 7.8 20 12l-5.6 4.2a1 1 0 0 0-.4.8v1.98c0 .21.24.33.4.2l8.1-6.4a1 1 0 0 0 0-1.56l-8.1-6.4a.25.25 0 0 0-.4.2V7a1 1 0 0 0 .4.8Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MoreIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-more-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M4 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm10-2a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm8 0a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SortIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-sort-icon")}
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M3.5 3.5a.5.5 0 0 0-1 0v8.793l-1.146-1.147a.5.5 0 0 0-.708.708l2 1.999.007.007a.497.497 0 0 0 .7-.006l2-2a.5.5 0 0 0-.707-.708L3.5 12.293zm4 .5a.5.5 0 0 1 0-1h1a.5.5 0 0 1 0 1zm0 3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm0 3a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1zM7 12.5a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 0-1h-7a.5.5 0 0 0-.5.5"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FolderIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-folder-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M2 5a3 3 0 0 1 3-3h3.93a2 2 0 0 1 1.66.9L12 5h7a3 3 0 0 1 3 3v11a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LogIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-log-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M3.11 8H6v10.82c0 .86.37 1.68 1 2.27.46.43 1.02.71 1.63.84A1 1 0 0 0 9 22h10a4 4 0 0 0 4-4v-1a2 2 0 0 0-2-2h-1V5a3 3 0 0 0-3-3H4.67c-.87 0-1.7.32-2.34.9-.63.6-1 1.42-1 2.28 0 .71.3 1.35.52 1.75a5.35 5.35 0 0 0 .48.7l.01.01h.01L3.11 7l-.76.65a1 1 0 0 0 .76.35Zm1.56-4c-.38 0-.72.14-.97.37-.24.23-.37.52-.37.81a1.69 1.69 0 0 0 .3.82H6v-.83c0-.29-.13-.58-.37-.8C5.4 4.14 5.04 4 4.67 4Zm5 13a3.58 3.58 0 0 1 0 3H19a2 2 0 0 0 2-2v-1H9.66ZM3.86 6.35ZM11 8a1 1 0 1 0 0 2h5a1 1 0 1 0 0-2h-5Zm-1 5a1 1 0 0 1 1-1h5a1 1 0 1 1 0 2h-5a1 1 0 0 1-1-1Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function RestartIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-restart-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M4 12a8 8 0 0 1 14.93-4H15a1 1 0 1 0 0 2h6a1 1 0 0 0 1-1V3a1 1 0 1 0-2 0v3a9.98 9.98 0 0 0-18 6 10 10 0 0 0 16.29 7.78 1 1 0 0 0-1.26-1.56A8 8 0 0 1 4 12Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PaintbrushIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-paintbrush-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M15.35 7.24C15.9 6.67 16 5.8 16 5a3 3 0 1 1 3 3c-.8 0-1.67.09-2.24.65a1.5 1.5 0 0 0 0 2.11l1.12 1.12a3 3 0 0 1 0 4.24l-5 5a3 3 0 0 1-4.25 0l-5.76-5.75a3 3 0 0 1 0-4.24l4.04-4.04.97-.97a3 3 0 0 1 4.24 0l1.12 1.12c.58.58 1.52.58 2.1 0ZM6.9 9.9 4.3 12.54a1 1 0 0 0 0 1.42l2.17 2.17.83-.84a1 1 0 0 1 1.42 1.42l-.84.83.59.59 1.83-1.84a1 1 0 0 1 1.42 1.42l-1.84 1.83.17.17a1 1 0 0 0 1.42 0l2.63-2.62L6.9 9.9Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PencilIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-pencil-icon")}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="m13.96 5.46 4.58 4.58a1 1 0 0 0 1.42 0l1.38-1.38a2 2 0 0 0 0-2.82l-3.18-3.18a2 2 0 0 0-2.82 0l-1.38 1.38a1 1 0 0 0 0 1.42ZM2.11 20.16l.73-4.22a3 3 0 0 1 .83-1.61l7.87-7.87a1 1 0 0 1 1.42 0l4.58 4.58a1 1 0 0 1 0 1.42l-7.87 7.87a3 3 0 0 1-1.6.83l-4.23.73a1.5 1.5 0 0 1-1.73-1.73Z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function InternetIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
{...props}
|
|
||||||
className={classes(props.className, "dc-internet-icon")}
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855q-.215.403-.395.872c.705.157 1.472.257 2.282.287zM4.249 3.539q.214-.577.481-1.078a7 7 0 0 1 .597-.933A7 7 0 0 0 3.051 3.05q.544.277 1.198.49zM3.509 7.5c.036-1.07.188-2.087.436-3.008a9 9 0 0 1-1.565-.667A6.96 6.96 0 0 0 1.018 7.5zm1.4-2.741a12.3 12.3 0 0 0-.4 2.741H7.5V5.091c-.91-.03-1.783-.145-2.591-.332M8.5 5.09V7.5h2.99a12.3 12.3 0 0 0-.399-2.741c-.808.187-1.681.301-2.591.332zM4.51 8.5c.035.987.176 1.914.399 2.741A13.6 13.6 0 0 1 7.5 10.91V8.5zm3.99 0v2.409c.91.03 1.783.145 2.591.332.223-.827.364-1.754.4-2.741zm-3.282 3.696q.18.469.395.872c.552 1.035 1.218 1.65 1.887 1.855V11.91c-.81.03-1.577.13-2.282.287zm.11 2.276a7 7 0 0 1-.598-.933 9 9 0 0 1-.481-1.079 8.4 8.4 0 0 0-1.198.49 7 7 0 0 0 2.276 1.522zm-1.383-2.964A13.4 13.4 0 0 1 3.508 8.5h-2.49a6.96 6.96 0 0 0 1.362 3.675c.47-.258.995-.482 1.565-.667m6.728 2.964a7 7 0 0 0 2.275-1.521 8.4 8.4 0 0 0-1.197-.49 9 9 0 0 1-.481 1.078 7 7 0 0 1-.597.933M8.5 11.909v3.014c.67-.204 1.335-.82 1.887-1.855q.216-.403.395-.872A12.6 12.6 0 0 0 8.5 11.91zm3.555-.401c.57.185 1.095.409 1.565.667A6.96 6.96 0 0 0 14.982 8.5h-2.49a13.4 13.4 0 0 1-.437 3.008M14.982 7.5a6.96 6.96 0 0 0-1.362-3.675c-.47.258-.995.482-1.565.667.248.92.4 1.938.437 3.008zM11.27 2.461q.266.502.482 1.078a8.4 8.4 0 0 0 1.196-.49 7 7 0 0 0-2.275-1.52c.218.283.418.597.597.932m-.488 1.343a8 8 0 0 0-.395-.872C9.835 1.897 9.17 1.282 8.5 1.077V4.09c.81-.03 1.577-.13 2.282-.287z"
|
|
||||||
/>
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,252 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, openModal, PluginProps, Toasts, useEffect, UserStore, useState, useStateFromStores } from "..";
|
|
||||||
import { ColorwayCSS } from "../colorwaysAPI";
|
|
||||||
import { generateCss, pureGradientBase } from "../css";
|
|
||||||
import { Colorway, ModalProps } from "../types";
|
|
||||||
import { colorToHex, saveFile, stringToHex } from "../utils";
|
|
||||||
import SaveColorwayModal from "./SaveColorwayModal";
|
|
||||||
import ThemePreview from "./ThemePreview";
|
|
||||||
|
|
||||||
function RenameColorwayModal({ modalProps, ogName, onFinish, colorwayList }: { modalProps: ModalProps, ogName: string, onFinish: (name: string) => void, colorwayList: Colorway[]; }) {
|
|
||||||
const [error, setError] = useState<string>("");
|
|
||||||
const [newName, setNewName] = useState<string>(ogName);
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Rename Colorway...
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
value={newName}
|
|
||||||
onInput={({ currentTarget: { value } }) => {
|
|
||||||
setNewName(value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
if (!newName) {
|
|
||||||
return setError("Error: Please enter a valid name");
|
|
||||||
}
|
|
||||||
if (colorwayList.map(c => c.name).includes(newName)) {
|
|
||||||
return setError("Error: Name already exists");
|
|
||||||
}
|
|
||||||
onFinish(newName);
|
|
||||||
modalProps.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => modalProps.onClose()}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
modalProps,
|
|
||||||
colorway,
|
|
||||||
loadUIProps
|
|
||||||
}: {
|
|
||||||
modalProps: ModalProps;
|
|
||||||
colorway: Colorway;
|
|
||||||
loadUIProps: () => Promise<void>;
|
|
||||||
}) {
|
|
||||||
const colors: string[] = colorway.colors || [
|
|
||||||
"accent",
|
|
||||||
"primary",
|
|
||||||
"secondary",
|
|
||||||
"tertiary",
|
|
||||||
];
|
|
||||||
const profile = useStateFromStores([UserStore], () => UserStore.getUser(colorway.authorID));
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Colorway: {colorway.name}
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<div style={{ gap: "8px", width: "100%", display: "flex", flexDirection: "column" }}>
|
|
||||||
<span className="colorwaysModalSectionHeader">Creator:</span>
|
|
||||||
<div style={{ gap: ".5rem", display: "flex" }}>
|
|
||||||
{<img src={`https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.webp?size=32`} width={32} height={32} style={{
|
|
||||||
borderRadius: "32px"
|
|
||||||
}} />}
|
|
||||||
<span className="colorwaysModalSectionHeader" style={{ lineHeight: "32px" }} onClick={() => {
|
|
||||||
navigator.clipboard.writeText(profile.username);
|
|
||||||
Toasts.show({
|
|
||||||
message: "Copied Colorway Author Username Successfully",
|
|
||||||
type: 1,
|
|
||||||
id: "copy-colorway-author-username-notify",
|
|
||||||
});
|
|
||||||
}}>{colorway.author}</span>
|
|
||||||
</div>
|
|
||||||
<span className="colorwaysModalSectionHeader">Colors:</span>
|
|
||||||
<div style={{ gap: "8px", display: "flex" }}>
|
|
||||||
{colors.map(color => <div className="colorwayInfo-colorSwatch" style={{ backgroundColor: colorway[color] }} />)}
|
|
||||||
</div>
|
|
||||||
<span className="colorwaysModalSectionHeader">Actions:</span>
|
|
||||||
<div style={{ gap: "8px", flexDirection: "column", display: "flex" }}>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={() => {
|
|
||||||
const colorwayIDArray = `${colorway.accent},${colorway.primary},${colorway.secondary},${colorway.tertiary}|n:${colorway.name}${colorway.preset ? `|p:${colorway.preset}` : ""}`;
|
|
||||||
const colorwayID = stringToHex(colorwayIDArray);
|
|
||||||
navigator.clipboard.writeText(colorwayID);
|
|
||||||
Toasts.show({
|
|
||||||
message: "Copied Colorway ID Successfully",
|
|
||||||
type: 1,
|
|
||||||
id: "copy-colorway-id-notify",
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy Colorway ID
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={() => {
|
|
||||||
navigator.clipboard.writeText(colorway["dc-import"]);
|
|
||||||
Toasts.show({
|
|
||||||
message: "Copied CSS to Clipboard",
|
|
||||||
type: 1,
|
|
||||||
id: "copy-colorway-css-notify",
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy CSS
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={async () => {
|
|
||||||
const newColorway = {
|
|
||||||
...colorway,
|
|
||||||
"dc-import": generateCss(colorToHex(colorway.primary) || "313338", colorToHex(colorway.secondary) || "2b2d31", colorToHex(colorway.tertiary) || "1e1f22", colorToHex(colorway.accent) || "5865f2", true, true, undefined, colorway.name)
|
|
||||||
};
|
|
||||||
openModal(props => <SaveColorwayModal modalProps={props} colorways={[newColorway]} onFinish={() => { }} />);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Update CSS
|
|
||||||
</button>
|
|
||||||
{colorway.sourceType === "offline" && <button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={async () => {
|
|
||||||
const offlineSources = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).map(o => o.colorways).filter(colorArr => colorArr.map(color => color.name).includes(colorway.name))[0];
|
|
||||||
openModal(props => <RenameColorwayModal ogName={colorway.name} colorwayList={offlineSources} modalProps={props} onFinish={async (newName: string) => {
|
|
||||||
const stores = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).map(source => {
|
|
||||||
if (source.name === colorway.source) {
|
|
||||||
return {
|
|
||||||
name: source.name,
|
|
||||||
colorways: [...source.colorways.filter(colorway => colorway.name !== colorway.name), {
|
|
||||||
...colorway,
|
|
||||||
name: newName
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
} else return source;
|
|
||||||
});
|
|
||||||
DataStore.set("customColorways", stores);
|
|
||||||
if ((await DataStore.get("activeColorwayObject")).id === colorway.name) {
|
|
||||||
DataStore.set("activeColorwayObject", { id: newName, css: colorway.name, sourceType: "offline", source: colorway.source });
|
|
||||||
}
|
|
||||||
modalProps.onClose();
|
|
||||||
loadUIProps();
|
|
||||||
}} />);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Rename
|
|
||||||
</button>}
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={() => {
|
|
||||||
if (!colorway["dc-import"].includes("@name")) {
|
|
||||||
saveFile(new File([`/**
|
|
||||||
* @name ${colorway.name || "Colorway"}
|
|
||||||
* @version ${PluginProps.creatorVersion}
|
|
||||||
* @description Automatically generated Colorway.
|
|
||||||
* @author ${UserStore.getCurrentUser().username}
|
|
||||||
* @authorId ${UserStore.getCurrentUser().id}
|
|
||||||
*/
|
|
||||||
${colorway["dc-import"].replace((colorway["dc-import"].match(/\/\*.+\*\//) || [""])[0], "").replaceAll("url(//", "url(https://").replaceAll("url(\"//", "url(\"https://")}`], `${colorway.name.replaceAll(" ", "-").toLowerCase()}.theme.css`, { type: "text/plain" }));
|
|
||||||
} else {
|
|
||||||
saveFile(new File([colorway["dc-import"]], `${colorway.name.replaceAll(" ", "-").toLowerCase()}.theme.css`, { type: "text/plain" }));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Download CSS
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={() => {
|
|
||||||
openModal((props: ModalProps) => <div className={`colorwaysPreview-modal ${props.transitionState === 2 ? "closing" : ""} ${props.transitionState === 4 ? "hidden" : ""}`}>
|
|
||||||
<style>
|
|
||||||
{colorway.isGradient ? pureGradientBase + `.colorwaysPreview-modal,.colorwaysPreview-wrapper {--gradient-theme-bg: linear-gradient(${colorway.linearGradient})}` : ""}
|
|
||||||
</style>
|
|
||||||
<ThemePreview
|
|
||||||
accent={colorway.accent}
|
|
||||||
primary={colorway.primary}
|
|
||||||
secondary={colorway.secondary}
|
|
||||||
tertiary={colorway.tertiary}
|
|
||||||
isModal
|
|
||||||
modalProps={props}
|
|
||||||
/>
|
|
||||||
</div>);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Show preview
|
|
||||||
</button>
|
|
||||||
{colorway.sourceType === "offline" && <button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={async () => {
|
|
||||||
const oldStores = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).filter(source => source.name !== colorway.source);
|
|
||||||
const storeToModify = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).filter(source => source.name === colorway.source)[0];
|
|
||||||
const newStore = { name: storeToModify.name, colorways: storeToModify.colorways.filter(colorway => colorway.name !== colorway.name) };
|
|
||||||
DataStore.set("customColorways", [...oldStores, newStore]);
|
|
||||||
if ((await DataStore.get("activeColorwayObject")).id === colorway.name) {
|
|
||||||
DataStore.set("activeColorwayObject", { id: null, css: null, sourceType: null, source: null });
|
|
||||||
ColorwayCSS.remove();
|
|
||||||
}
|
|
||||||
modalProps.onClose();
|
|
||||||
loadUIProps();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</button>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, useEffect, useState } from "..";
|
|
||||||
import { ModalProps } from "../types";
|
|
||||||
import { hexToString } from "../utils";
|
|
||||||
|
|
||||||
export default function ({ modalProps, onColorwayId }: { modalProps: ModalProps, onColorwayId: (colorwayID: string) => void; }) {
|
|
||||||
const [colorwayID, setColorwayID] = useState<string>("");
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<span className="colorwaysModalSectionHeader">Colorway ID:</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
placeholder="Enter Colorway ID"
|
|
||||||
onInput={({ currentTarget: { value } }) => setColorwayID(value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
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
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => modalProps.onClose()}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { MouseEvent, MouseEventHandler } from "react";
|
|
||||||
|
|
||||||
import { DataStore, useEffect, useRef, useState } from "../";
|
|
||||||
import { ModalProps } from "../types";
|
|
||||||
import { restartWS, updateRemoteSources, wsOpen } from "../wsClient";
|
|
||||||
// eslint-disable-next-line no-duplicate-imports
|
|
||||||
import { boundKey as bk } from "../wsClient";
|
|
||||||
import Selector from "./Selector";
|
|
||||||
import SettingsPage from "./SettingsTabs/SettingsPage";
|
|
||||||
import SourceManager from "./SettingsTabs/SourceManager";
|
|
||||||
import Store from "./SettingsTabs/Store";
|
|
||||||
|
|
||||||
export let changeTheme = (theme: string) => { };
|
|
||||||
export let updateWSMain: (status: boolean) => void = () => { };
|
|
||||||
export let updateBoundKeyMain: (boundKey: { [managerKey: string]: string; }) => void = () => { };
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
modalProps
|
|
||||||
}: {
|
|
||||||
modalProps: ModalProps;
|
|
||||||
}): JSX.Element | any {
|
|
||||||
const [activeTab, setActiveTab] = useState<"selector" | "settings" | "sources" | "store" | "ws_connection">("selector");
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
const [pos, setPos] = useState({ x: 0, y: 0 });
|
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
|
||||||
const [wsConnected, setWsConnected] = useState(wsOpen);
|
|
||||||
const [boundKey, setBoundKey] = useState<{ [managerKey: string]: string; }>(bk as { [managerKey: string]: string; });
|
|
||||||
const menuProps = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
updateWSMain = status => setWsConnected(status);
|
|
||||||
changeTheme = (theme: string) => setTheme(theme);
|
|
||||||
updateBoundKeyMain = bound => setBoundKey(bound);
|
|
||||||
load();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
updateWSMain = () => { };
|
|
||||||
changeTheme = () => { };
|
|
||||||
updateBoundKeyMain = () => { };
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function SidebarTab({ id, title, icon, bottom }: { id: "selector" | "settings" | "sources" | "store" | "ws_connection", title: string, icon: JSX.Element, bottom?: boolean; }) {
|
|
||||||
return <div className={"colorwaySelectorSidebar-tab" + (id === activeTab ? " active" : "")} style={bottom ? { marginTop: "auto" } : {}} onClick={!bottom ? ((() => setActiveTab(id)) as unknown as MouseEventHandler<HTMLDivElement>) : rightClickContextMenu}>{icon}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rightClickContextMenu: MouseEventHandler<HTMLDivElement> = (e: MouseEvent<HTMLDivElement>) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
window.dispatchEvent(new Event("click"));
|
|
||||||
setShowMenu(!showMenu);
|
|
||||||
setPos({
|
|
||||||
x: e.currentTarget.getBoundingClientRect().x + e.currentTarget.offsetWidth + 8,
|
|
||||||
y: e.currentTarget.getBoundingClientRect().y + e.currentTarget.offsetHeight - (menuProps.current as unknown as HTMLElement).offsetHeight
|
|
||||||
});
|
|
||||||
};
|
|
||||||
function onPageClick(this: Window, e: globalThis.MouseEvent) {
|
|
||||||
setShowMenu(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener("click", onPageClick);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("click", onPageClick);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={`colorwaySelectorModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme} {...modalProps}>
|
|
||||||
<div className="colorwaySelectorSidebar">
|
|
||||||
<SidebarTab icon={<></>} id="selector" title="Change Colorway" />
|
|
||||||
<SidebarTab icon={<></>} id="settings" title="Settings" />
|
|
||||||
<SidebarTab icon={<></>} id="sources" title="Sources" />
|
|
||||||
<SidebarTab icon={<></>} id="store" title="Store" />
|
|
||||||
<SidebarTab bottom icon={<></>} id="ws_connection" title="Manager Connection" />
|
|
||||||
</div>
|
|
||||||
<div className="colorwayModalContent">
|
|
||||||
{activeTab === "selector" && <Selector />}
|
|
||||||
{activeTab === "sources" && <SourceManager />}
|
|
||||||
{activeTab === "store" && <Store />}
|
|
||||||
{activeTab === "settings" && <div style={{ padding: "16px" }}><SettingsPage /></div>}
|
|
||||||
</div>
|
|
||||||
<div ref={menuProps} className={`colorwaysManagerConnectionMenu ${showMenu ? "visible" : ""}`} style={{
|
|
||||||
position: "fixed",
|
|
||||||
top: `${pos.y}px`,
|
|
||||||
left: `${pos.x}px`
|
|
||||||
}}>
|
|
||||||
<span>Manager Connection Status: {wsConnected ? "Connected" : "Disconnected"}</span>
|
|
||||||
{wsConnected ? <>
|
|
||||||
<span className="colorwaysManagerConnectionValue">Bound Key: <b>{JSON.stringify(boundKey)}</b></span>
|
|
||||||
<button className="colorwaysPillButton" style={{
|
|
||||||
marginTop: "4px"
|
|
||||||
}} onClick={() => navigator.clipboard.writeText(JSON.stringify(boundKey))}>Copy Bound Key</button>
|
|
||||||
<button className="colorwaysPillButton" style={{
|
|
||||||
marginTop: "4px"
|
|
||||||
}} onClick={restartWS}>Reset Connection</button>
|
|
||||||
<button className="colorwaysPillButton" style={{
|
|
||||||
marginTop: "4px"
|
|
||||||
}} onClick={updateRemoteSources}>Update Remote Sources</button>
|
|
||||||
</> : <></>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, useEffect, useState } from "..";
|
|
||||||
import { ModalProps } from "../types";
|
|
||||||
|
|
||||||
export default function ({ modalProps }: { modalProps: ModalProps; }) {
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Project Colorway has moved
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<span style={{ maxWidth: "600px", color: "var(--text-normal)" }}>
|
|
||||||
In the process of creating a more solid foundation
|
|
||||||
for Project Colorway, the main Project Colorway repository has been
|
|
||||||
moved from <a role="link" target="_blank" href="https://github.com/DaBluLite/ProjectColorway">https://github.com/DaBluLite/ProjectColorway</a> to{" "}
|
|
||||||
<a role="link" target="_blank" href="https://github.com/ProjectColorway/ProjectColorway">https://github.com/ProjectColorway/ProjectColorway</a>
|
|
||||||
</span>
|
|
||||||
<br />
|
|
||||||
<span style={{ textAlign: "center", color: "var(--text-normal)" }}>The default Project Colorway source has been automatically updated/re-added.</span>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { MouseEvent } from "react";
|
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from "..";
|
|
||||||
|
|
||||||
export default function ({ onClick, onForceReload }: { onClick: () => void, onForceReload: () => void; }) {
|
|
||||||
const menuProps = useRef(null);
|
|
||||||
const [pos, setPos] = useState({ x: 0, y: 0 });
|
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
|
||||||
|
|
||||||
function rightClickContextMenu(e: MouseEvent<HTMLButtonElement, MouseEvent>) {
|
|
||||||
e.stopPropagation();
|
|
||||||
window.dispatchEvent(new Event("click"));
|
|
||||||
setShowMenu(!showMenu);
|
|
||||||
setPos({
|
|
||||||
x: e.currentTarget.getBoundingClientRect().x,
|
|
||||||
y: e.currentTarget.getBoundingClientRect().y + e.currentTarget.offsetHeight + 8
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function onPageClick(this: Window, e: globalThis.MouseEvent) {
|
|
||||||
setShowMenu(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener("click", onPageClick);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("click", onPageClick);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function onForceReload_internal() {
|
|
||||||
onForceReload();
|
|
||||||
setShowMenu(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>
|
|
||||||
{showMenu ? <nav className="colorwaysContextMenu" ref={menuProps} style={{
|
|
||||||
position: "fixed",
|
|
||||||
top: `${pos.y}px`,
|
|
||||||
left: `${pos.x}px`
|
|
||||||
}}>
|
|
||||||
<button onClick={onForceReload_internal} className="colorwaysContextMenuItm">
|
|
||||||
Force Refresh
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
style={{ boxSizing: "content-box", marginLeft: "8px" }}
|
|
||||||
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>
|
|
||||||
</nav> : null}
|
|
||||||
<button className="colorwaysPillButton" onContextMenu={() => rightClickContextMenu} onClick={onClick}>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
width="14"
|
|
||||||
height="14"
|
|
||||||
style={{ 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>
|
|
||||||
Refresh
|
|
||||||
</button>
|
|
||||||
</>;
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, openModal, useEffect, useState } from "..";
|
|
||||||
import { Colorway, ModalProps } from "../types";
|
|
||||||
import { PlusIcon } from "./Icons";
|
|
||||||
import { StoreNameModal } from "./SettingsTabs/SourceManager";
|
|
||||||
|
|
||||||
export default function ({ modalProps, colorways, onFinish }: { modalProps: ModalProps, colorways: Colorway[], onFinish: () => void; }) {
|
|
||||||
const [offlineColorwayStores, setOfflineColorwayStores] = useState<{ name: string, colorways: Colorway[], id?: string; }[]>([]);
|
|
||||||
const [storename, setStorename] = useState<string>();
|
|
||||||
const [noStoreError, setNoStoreError] = useState<boolean>(false);
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
setOfflineColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]);
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Save to source:
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
{noStoreError ? <span style={{ color: "var(--text-danger)" }}>Error: No store selected</span> : <></>}
|
|
||||||
{offlineColorwayStores.map(store => <div
|
|
||||||
className="discordColorway"
|
|
||||||
style={{ padding: "10px" }}
|
|
||||||
aria-checked={storename === store.name}
|
|
||||||
onClick={() => {
|
|
||||||
setStorename(store.name);
|
|
||||||
}}>
|
|
||||||
<svg aria-hidden="true" role="img" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
|
||||||
{storename === store.name && <circle cx="12" cy="12" r="5" className="radioIconForeground-3wH3aU" fill="currentColor" />}
|
|
||||||
</svg>
|
|
||||||
<span className="colorwayLabel">{store.name}</span>
|
|
||||||
</div>)}
|
|
||||||
<div
|
|
||||||
className="discordColorway"
|
|
||||||
style={{ padding: "10px" }}
|
|
||||||
onClick={() => {
|
|
||||||
openModal(props => <StoreNameModal modalProps={props} conflicting={false} originalName="" onFinish={async e => {
|
|
||||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: [] }]);
|
|
||||||
setOfflineColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
|
||||||
}} />);
|
|
||||||
}}>
|
|
||||||
<PlusIcon width={24} height={24} />
|
|
||||||
<span className="colorwayLabel">Create new store...</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
setNoStoreError(false);
|
|
||||||
if (!storename) {
|
|
||||||
setNoStoreError(true);
|
|
||||||
} else {
|
|
||||||
const oldStores: { name: string, colorways: Colorway[], id?: string; }[] | undefined = await DataStore.get("customColorways");
|
|
||||||
const storeToModify: { name: string, colorways: Colorway[], id?: string; } | undefined = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).filter(source => source.name === storename)[0];
|
|
||||||
colorways.map((colorway, i) => {
|
|
||||||
if (storeToModify.colorways.map(colorway => colorway.name).includes(colorway.name)) {
|
|
||||||
openModal(props => <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Duplicate Colorway
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<span className="colorwaysModalSectionHeader">A colorway with the same name was found in this store, what do you want to do?</span>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={() => {
|
|
||||||
const newStore = { name: storeToModify.name, colorways: [...storeToModify.colorways.filter(colorwayy => colorwayy.name !== colorway.name), colorway] };
|
|
||||||
DataStore.set("customColorways", [...oldStores!.filter(source => source.name !== storename), newStore]);
|
|
||||||
props.onClose();
|
|
||||||
if (i + 1 === colorways.length) {
|
|
||||||
modalProps.onClose();
|
|
||||||
onFinish!();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Override
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={() => {
|
|
||||||
function NewColorwayNameModal({ modalProps, onSelected }: { modalProps: ModalProps, onSelected: (e: string) => void; }) {
|
|
||||||
const [errorMsg, setErrorMsg] = useState<string>();
|
|
||||||
const [newColorwayName, setNewColorwayName] = useState("");
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Select new name
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
value={newColorwayName}
|
|
||||||
onInput={({ currentTarget: { value } }) => setNewColorwayName(value)}
|
|
||||||
placeholder="Enter valid colorway name" />
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
setErrorMsg("");
|
|
||||||
if (storeToModify!.colorways.map(colorway => colorway.name).includes(newColorwayName)) {
|
|
||||||
setErrorMsg("Error: Name already exists");
|
|
||||||
} else {
|
|
||||||
onSelected(newColorwayName);
|
|
||||||
if (i + 1 === colorways.length) {
|
|
||||||
modalProps.onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
if (i + 1 === colorways.length) {
|
|
||||||
modalProps.onClose();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
openModal(propss => <NewColorwayNameModal modalProps={propss} onSelected={e => {
|
|
||||||
const newStore = { name: storeToModify.name, colorways: [...storeToModify.colorways, { ...colorway, name: e }] };
|
|
||||||
DataStore.set("customColorways", [...oldStores!.filter(source => source.name !== storename), newStore]);
|
|
||||||
props.onClose();
|
|
||||||
if (i + 1 === colorways.length) {
|
|
||||||
modalProps.onClose();
|
|
||||||
onFinish!();
|
|
||||||
}
|
|
||||||
}} />);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Rename
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
props.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Select different store
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
} else {
|
|
||||||
const newStore = { name: storeToModify.name, colorways: [...storeToModify.colorways, colorway] };
|
|
||||||
DataStore.set("customColorways", [...oldStores!.filter(source => source.name !== storename), newStore]);
|
|
||||||
if (i + 1 === colorways.length) {
|
|
||||||
modalProps.onClose();
|
|
||||||
onFinish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
modalProps.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,563 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, ReactNode, Toasts } from "..";
|
|
||||||
import { openModal, useEffect, useState } from "../";
|
|
||||||
import { ColorwayCSS } from "../colorwaysAPI";
|
|
||||||
import { nullColorwayObj } from "../constants";
|
|
||||||
import { generateCss, getAutoPresets, gradientBase } from "../css";
|
|
||||||
import { Colorway, ColorwayObject, ModalProps, SortOptions, SourceObject } from "../types";
|
|
||||||
import { colorToHex, getHex, stringToHex } from "../utils";
|
|
||||||
import { hasManagerRole, requestManagerRole, sendColorway, updateRemoteSources, wsOpen } from "../wsClient";
|
|
||||||
import AutoColorwaySelector from "./AutoColorwaySelector";
|
|
||||||
import CreatorModal from "./CreatorModal";
|
|
||||||
import FiltersMenu from "./FiltersMenu";
|
|
||||||
import { CodeIcon, DeleteIcon, IDIcon, PalleteIcon, PlusIcon } from "./Icons";
|
|
||||||
import InfoModal from "./InfoModal";
|
|
||||||
import ReloadButton from "./ReloadButton";
|
|
||||||
import SourcesMenu from "./SourcesMenu";
|
|
||||||
import UseRepainterThemeModal from "./UseRepainterThemeModal";
|
|
||||||
|
|
||||||
export let updateWS: (status: boolean) => void = () => { };
|
|
||||||
export let updateManagerRole: (hasManager: boolean) => void = () => { };
|
|
||||||
export let updateActiveColorway: (active: ColorwayObject) => void = () => { };
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
settings = { selectorType: "normal" },
|
|
||||||
hasTheme = false
|
|
||||||
}: {
|
|
||||||
settings?: { selectorType: "preview" | "multiple-selection" | "normal", previewSource?: string, onSelected?: (colorways: Colorway[]) => void; };
|
|
||||||
hasTheme?: boolean;
|
|
||||||
}) {
|
|
||||||
const [colorwayData, setColorwayData] = useState<SourceObject[]>([]);
|
|
||||||
const [searchValue, setSearchValue] = useState<string>("");
|
|
||||||
const [sortBy, setSortBy] = useState<SortOptions>(SortOptions.NAME_AZ);
|
|
||||||
const [activeColorwayObject, setActiveColorwayObject] = useState<ColorwayObject>(nullColorwayObj);
|
|
||||||
const [customColorwayData, setCustomColorwayData] = useState<SourceObject[]>([]);
|
|
||||||
const [loaderHeight, setLoaderHeight] = useState<"2px" | "0px">("2px");
|
|
||||||
const [visibleSources, setVisibleSources] = useState<string>("all");
|
|
||||||
const [selectedColorways, setSelectedColorways] = useState<Colorway[]>([]);
|
|
||||||
const [errorCode, setErrorCode] = useState<number>(0);
|
|
||||||
const [wsConnected, setWsConnected] = useState(wsOpen);
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
const [isManager, setManager] = useState<boolean>(hasManagerRole);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
updateWS = status => {
|
|
||||||
setWsConnected(status);
|
|
||||||
};
|
|
||||||
console.log(hasManagerRole);
|
|
||||||
updateManagerRole = hasManager => {
|
|
||||||
setManager(hasManager);
|
|
||||||
console.log(hasManager);
|
|
||||||
};
|
|
||||||
updateActiveColorway = setActiveColorwayObject;
|
|
||||||
return () => {
|
|
||||||
updateWS = () => { };
|
|
||||||
updateManagerRole = () => { };
|
|
||||||
updateActiveColorway = () => { };
|
|
||||||
};
|
|
||||||
}, [isManager]);
|
|
||||||
|
|
||||||
const filters = [
|
|
||||||
{
|
|
||||||
name: "All",
|
|
||||||
id: "all",
|
|
||||||
sources: [...colorwayData, ...customColorwayData]
|
|
||||||
},
|
|
||||||
...colorwayData.map(source => ({
|
|
||||||
name: source.source,
|
|
||||||
id: source.source.toLowerCase().replaceAll(" ", "-"),
|
|
||||||
sources: [source]
|
|
||||||
})),
|
|
||||||
...customColorwayData.map(source => ({
|
|
||||||
name: source.source,
|
|
||||||
id: source.source.toLowerCase().replaceAll(" ", "-"),
|
|
||||||
sources: [source]
|
|
||||||
}))
|
|
||||||
];
|
|
||||||
|
|
||||||
async function loadUI(force?: boolean) {
|
|
||||||
setActiveColorwayObject(await DataStore.get("activeColorwayObject") as ColorwayObject);
|
|
||||||
setLoaderHeight("0px");
|
|
||||||
|
|
||||||
if (settings.previewSource) {
|
|
||||||
|
|
||||||
const res: Response = await fetch(settings.previewSource);
|
|
||||||
|
|
||||||
const dataPromise = res.json().then(data => data).catch(() => ({ colorways: [], errorCode: 1, errorMsg: "Colorway Source format is invalid" }));
|
|
||||||
|
|
||||||
const data = await dataPromise;
|
|
||||||
|
|
||||||
if (data.errorCode) {
|
|
||||||
setErrorCode(data.errorCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
const colorwayList: Colorway[] = data.css ? data.css.map(customStore => customStore.colorways).flat() : data.colorways;
|
|
||||||
|
|
||||||
setColorwayData([{ colorways: colorwayList || [], source: res.url, type: "online" }] as { type: "online" | "offline" | "temporary", source: string, colorways: Colorway[]; }[]);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
setCustomColorwayData((await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).map((colorSrc: { name: string, colorways: Colorway[], id?: string; }) => ({ type: "offline", source: colorSrc.name, colorways: colorSrc.colorways })));
|
|
||||||
|
|
||||||
const onlineSources: { name: string, url: string; }[] = await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[];
|
|
||||||
|
|
||||||
const responses: Response[] = await Promise.all(
|
|
||||||
onlineSources.map(source =>
|
|
||||||
fetch(source.url, force ? { cache: "no-store" } : {})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
setColorwayData(await Promise.all(
|
|
||||||
responses
|
|
||||||
.map((res, i) => ({ response: res, name: onlineSources[i].name }))
|
|
||||||
.map((res: { response: Response, name: string; }) =>
|
|
||||||
res.response.json().then(dt => ({ colorways: dt.colorways as Colorway[], source: res.name, type: "online" })).catch(() => ({ colorways: [] as Colorway[], source: res.name, type: "online" }))
|
|
||||||
)) as { type: "online" | "offline" | "temporary", source: string, colorways: Colorway[]; }[]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => { loadUI(); }, [searchValue]);
|
|
||||||
|
|
||||||
function Header({ children }: { children: ReactNode; }) {
|
|
||||||
if (hasTheme) return <div className="colorwayModal-selectorHeader" data-theme={theme}>{children}</div>;
|
|
||||||
else return <div className="colorwayModal-selectorHeader">{children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Container({ children }: { children: ReactNode; }) {
|
|
||||||
if (hasTheme) return <div style={{ maxHeight: settings.selectorType === "multiple-selection" ? "50%" : "unset" }} className="ColorwaySelectorWrapper" data-theme={theme}>{children}</div>;
|
|
||||||
else return <div style={{ maxHeight: settings.selectorType === "multiple-selection" ? "50%" : "unset" }} className="ColorwaySelectorWrapper">{children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{(settings.selectorType !== "preview" && (!wsConnected || (wsConnected && isManager))) ? <Header>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
placeholder="Search for Colorways..."
|
|
||||||
value={searchValue}
|
|
||||||
autoFocus
|
|
||||||
onInput={({ currentTarget: { value } }) => setSearchValue(value)}
|
|
||||||
/>
|
|
||||||
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
|
|
||||||
<ReloadButton onClick={() => {
|
|
||||||
setLoaderHeight("2px");
|
|
||||||
loadUI().then(() => setLoaderHeight("0px"));
|
|
||||||
}} onForceReload={() => {
|
|
||||||
setLoaderHeight("2px");
|
|
||||||
loadUI(true).then(() => setLoaderHeight("0px"));
|
|
||||||
}} />
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
openModal(props => <CreatorModal
|
|
||||||
modalProps={props}
|
|
||||||
loadUIProps={loadUI}
|
|
||||||
/>);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PlusIcon width={14} height={14} style={{ boxSizing: "content-box" }} />
|
|
||||||
Create
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
id="colorway-userepaintertheme"
|
|
||||||
onClick={() => {
|
|
||||||
openModal(props => <UseRepainterThemeModal modalProps={props} onFinish={async ({ id, colors }) => {
|
|
||||||
const demandedColorway = generateCss(colors[7].replace("#", ""), colors[11].replace("#", ""), colors[14].replace("#", ""), colors[16].replace("#", ""));
|
|
||||||
ColorwayCSS.set(demandedColorway);
|
|
||||||
const newObj: ColorwayObject = {
|
|
||||||
id: id!,
|
|
||||||
css: demandedColorway,
|
|
||||||
sourceType: "temporary",
|
|
||||||
source: "Repainter",
|
|
||||||
colors: {
|
|
||||||
accent: colors![16],
|
|
||||||
primary: colors![2],
|
|
||||||
secondary: colors![5],
|
|
||||||
tertiary: colors![8]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
DataStore.set("activeColorwayObject", newObj);
|
|
||||||
setActiveColorwayObject(newObj);
|
|
||||||
}} />);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PalleteIcon width={14} height={14} style={{ boxSizing: "content-box" }} />
|
|
||||||
Use Repainter theme
|
|
||||||
</button>
|
|
||||||
<FiltersMenu sort={sortBy} onSortChange={newSort => {
|
|
||||||
setSortBy(newSort);
|
|
||||||
}} />
|
|
||||||
<SourcesMenu source={filters.filter(filter => filter.id === visibleSources)[0]} sources={filters} onSourceChange={sourceId => {
|
|
||||||
setVisibleSources(sourceId);
|
|
||||||
}} />
|
|
||||||
</div>
|
|
||||||
</Header> : <></>}
|
|
||||||
{(wsConnected && settings.selectorType === "normal" && !isManager) ? <span style={{
|
|
||||||
color: "#fff",
|
|
||||||
margin: "auto",
|
|
||||||
fontWeight: "bold",
|
|
||||||
display: "flex",
|
|
||||||
gap: "8px",
|
|
||||||
alignItems: "center"
|
|
||||||
}}>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" fill="currentColor" viewBox="0 0 16 16" style={{
|
|
||||||
transform: "scaleX(1.2)"
|
|
||||||
}}>
|
|
||||||
<path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2m3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2" />
|
|
||||||
</svg>
|
|
||||||
Manager is controlling the colorways
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={requestManagerRole}
|
|
||||||
>
|
|
||||||
Request Manager
|
|
||||||
</button>
|
|
||||||
</span> : <>
|
|
||||||
<div className="colorwaysLoader-barContainer"><div className="colorwaysLoader-bar" style={{ height: loaderHeight }} /></div>
|
|
||||||
<Container>
|
|
||||||
{(activeColorwayObject.sourceType === "temporary" && settings.selectorType === "normal" && settings.selectorType === "normal") && <div
|
|
||||||
className="discordColorway"
|
|
||||||
id="colorway-Temporary"
|
|
||||||
aria-checked={activeColorwayObject.id === "Auto" && activeColorwayObject.source === null}
|
|
||||||
onClick={async () => {
|
|
||||||
DataStore.set("activeColorwayObject", nullColorwayObj);
|
|
||||||
setActiveColorwayObject(nullColorwayObj);
|
|
||||||
ColorwayCSS.remove();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="discordColorwayPreviewColorContainer">
|
|
||||||
<div
|
|
||||||
className="discordColorwayPreviewColor"
|
|
||||||
style={{ backgroundColor: "var(--brand-500)" }} />
|
|
||||||
<div
|
|
||||||
className="discordColorwayPreviewColor"
|
|
||||||
style={{ backgroundColor: "var(--background-primary)" }} />
|
|
||||||
<div
|
|
||||||
className="discordColorwayPreviewColor"
|
|
||||||
style={{ backgroundColor: "var(--background-secondary)" }} />
|
|
||||||
<div
|
|
||||||
className="discordColorwayPreviewColor"
|
|
||||||
style={{ backgroundColor: "var(--background-tertiary)" }} />
|
|
||||||
</div>
|
|
||||||
<span className="colorwayLabel">Temporary Colorway</span>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
openModal(props => <CreatorModal modalProps={props} colorwayID={`#${colorToHex(getHex(getComputedStyle(document.body).getPropertyValue("--brand-500")))},#${colorToHex(getHex(getComputedStyle(document.body).getPropertyValue("--primary-600")))},#${colorToHex(getHex(getComputedStyle(document.body).getPropertyValue("--primary-630")))},#${colorToHex(getHex(getComputedStyle(document.body).getPropertyValue("--primary-700")))}`} loadUIProps={loadUI} />);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PlusIcon width={20} height={20} />
|
|
||||||
</button>
|
|
||||||
</div>}
|
|
||||||
{getComputedStyle(document.body).getPropertyValue("--os-accent-color") && ["all", "official"].includes(visibleSources) && settings.selectorType === "normal" && "auto".includes(searchValue.toLowerCase()) ? <div
|
|
||||||
className="discordColorway"
|
|
||||||
id="colorway-Auto"
|
|
||||||
aria-checked={activeColorwayObject.id === "Auto" && activeColorwayObject.source === null}
|
|
||||||
onClick={async () => {
|
|
||||||
if (isManager) {
|
|
||||||
Toasts.show({
|
|
||||||
message: "Cannot use Auto colorway while on manager mode",
|
|
||||||
type: 2,
|
|
||||||
id: "colorways-manager-role-auto-colorway-error"
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const activeAutoPreset = await DataStore.get("activeAutoPreset");
|
|
||||||
if (activeColorwayObject.id === "Auto") {
|
|
||||||
DataStore.set("activeColorwayObject", nullColorwayObj);
|
|
||||||
setActiveColorwayObject(nullColorwayObj);
|
|
||||||
ColorwayCSS.remove();
|
|
||||||
} else {
|
|
||||||
if (!activeAutoPreset) {
|
|
||||||
openModal((props: ModalProps) => <AutoColorwaySelector autoColorwayId="" modalProps={props} onChange={autoPresetId => {
|
|
||||||
const demandedColorway = getAutoPresets(colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6))[autoPresetId].preset();
|
|
||||||
ColorwayCSS.set(demandedColorway);
|
|
||||||
const newObj: ColorwayObject = {
|
|
||||||
id: "Auto",
|
|
||||||
css: demandedColorway,
|
|
||||||
sourceType: "online",
|
|
||||||
source: null,
|
|
||||||
colors: {
|
|
||||||
accent: colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
DataStore.set("activeColorwayObject", newObj);
|
|
||||||
setActiveColorwayObject(newObj);
|
|
||||||
}} />);
|
|
||||||
} else {
|
|
||||||
const autoColorway = getAutoPresets(colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6))[activeAutoPreset].preset();
|
|
||||||
const newObj: ColorwayObject = {
|
|
||||||
id: "Auto",
|
|
||||||
css: autoColorway,
|
|
||||||
sourceType: "online",
|
|
||||||
source: null,
|
|
||||||
colors: {
|
|
||||||
accent: colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
DataStore.set("activeColorwayObject", newObj);
|
|
||||||
setActiveColorwayObject(newObj);
|
|
||||||
ColorwayCSS.set(autoColorway);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="discordColorwayPreviewColorContainer" style={{ backgroundColor: "var(--os-accent-color)" }} />
|
|
||||||
<span className="colorwayLabel">Auto Colorway</span>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
const activeAutoPreset = await DataStore.get("activeAutoPreset");
|
|
||||||
openModal((props: ModalProps) => <AutoColorwaySelector autoColorwayId={activeAutoPreset} modalProps={props} onChange={autoPresetId => {
|
|
||||||
if (activeColorwayObject.id === "Auto") {
|
|
||||||
const demandedColorway = getAutoPresets(colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6))[autoPresetId].preset();
|
|
||||||
const newObj: ColorwayObject = {
|
|
||||||
id: "Auto",
|
|
||||||
css: demandedColorway,
|
|
||||||
sourceType: "online",
|
|
||||||
source: null,
|
|
||||||
colors: {
|
|
||||||
accent: colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
DataStore.set("activeColorwayObject", newObj);
|
|
||||||
setActiveColorwayObject(newObj);
|
|
||||||
ColorwayCSS.set(demandedColorway);
|
|
||||||
}
|
|
||||||
}} />);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" style={{ margin: "4px" }} viewBox="0 0 24 24" fill="currentColor">
|
|
||||||
<path d="M 21.2856,9.6 H 24 v 4.8 H 21.2868 C 20.9976,15.5172 20.52,16.5576 19.878,17.4768 L 21.6,19.2 19.2,21.6 17.478,19.8768 c -0.9216,0.642 -1.9596,1.1208 -3.078,1.4088 V 24 H 9.6 V 21.2856 C 8.4828,20.9976 7.4436,20.5188 6.5232,19.8768 L 4.8,21.6 2.4,19.2 4.1232,17.4768 C 3.4812,16.5588 3.0024,15.5184 2.7144,14.4 H 0 V 9.6 H 2.7144 C 3.0024,8.4816 3.48,7.4424 4.1232,6.5232 L 2.4,4.8 4.8,2.4 6.5232,4.1232 C 7.4424,3.48 8.4816,3.0024 9.6,2.7144 V 0 h 4.8 v 2.7132 c 1.1184,0.2892 2.1564,0.7668 3.078,1.4088 l 1.722,-1.7232 2.4,2.4 -1.7232,1.7244 c 0.642,0.9192 1.1208,1.9596 1.4088,3.0768 z M 12,16.8 c 2.65092,0 4.8,-2.14908 4.8,-4.8 0,-2.650968 -2.14908,-4.8 -4.8,-4.8 -2.650968,0 -4.8,2.149032 -4.8,4.8 0,2.65092 2.149032,4.8 4.8,4.8 z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div> : <></>}
|
|
||||||
{(!getComputedStyle(document.body).getPropertyValue("--os-accent-color") || !["all", "official"].includes(visibleSources)) && !filters.filter(filter => filter.id === visibleSources)[0].sources.map(source => source.colorways).flat().length ? <span style={{
|
|
||||||
color: "#fff",
|
|
||||||
margin: "auto",
|
|
||||||
fontWeight: "bold",
|
|
||||||
display: "flex",
|
|
||||||
gap: "8px",
|
|
||||||
alignItems: "center"
|
|
||||||
}}>
|
|
||||||
No colorways...
|
|
||||||
</span> : <></>}
|
|
||||||
{errorCode !== 0 && <span style={{
|
|
||||||
color: "#fff",
|
|
||||||
margin: "auto",
|
|
||||||
fontWeight: "bold",
|
|
||||||
display: "flex",
|
|
||||||
gap: "8px",
|
|
||||||
alignItems: "center"
|
|
||||||
}}>
|
|
||||||
{errorCode === 1 && "Error: Invalid Colorway Source Format. If this error persists, contact the source author to resolve the issue."}
|
|
||||||
</span>}
|
|
||||||
{filters.map(filter => filter.id).includes(visibleSources) && (
|
|
||||||
filters
|
|
||||||
.filter(filter => filter.id === visibleSources)[0].sources
|
|
||||||
.map(({ colorways, source, type }) => colorways.map((colorway: Colorway) => ({ ...colorway, sourceType: type, source: source, preset: colorway.preset || (colorway.isGradient ? "Gradient" : "Default") })))
|
|
||||||
.flat()
|
|
||||||
.sort((a, b) => {
|
|
||||||
switch (sortBy) {
|
|
||||||
case SortOptions.NAME_AZ:
|
|
||||||
return a.name.localeCompare(b.name);
|
|
||||||
case SortOptions.NAME_ZA:
|
|
||||||
return b.name.localeCompare(a.name);
|
|
||||||
case SortOptions.SOURCE_AZ:
|
|
||||||
return a.source.localeCompare(b.source);
|
|
||||||
case SortOptions.SOURCE_ZA:
|
|
||||||
return b.source.localeCompare(a.source);
|
|
||||||
default:
|
|
||||||
return a.name.localeCompare(b.name);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map((color: Colorway) => color.colors ? color : { ...color, colors: ["accent", "primary", "secondary", "tertiary"] })
|
|
||||||
.map((color: Colorway) => {
|
|
||||||
const colors: { accent?: string, primary?: string, secondary?: string, tertiary?: string; } = {};
|
|
||||||
color.colors!.map(colorStr => colors[colorStr] = colorToHex(color[colorStr]));
|
|
||||||
return { ...color, colorObj: colors };
|
|
||||||
})
|
|
||||||
.map((color: Colorway) => {
|
|
||||||
return (color.name.toLowerCase().includes(searchValue.toLowerCase()) ?
|
|
||||||
<div
|
|
||||||
className="discordColorway"
|
|
||||||
id={"colorway-" + color.name}
|
|
||||||
aria-checked={activeColorwayObject.id === color.name && activeColorwayObject.source === color.source}
|
|
||||||
onClick={async () => {
|
|
||||||
if (settings.selectorType === "normal") {
|
|
||||||
const [
|
|
||||||
onDemandWays,
|
|
||||||
onDemandWaysTintedText,
|
|
||||||
onDemandWaysDiscordSaturation,
|
|
||||||
onDemandWaysOsAccentColor
|
|
||||||
] = await DataStore.getMany([
|
|
||||||
"onDemandWays",
|
|
||||||
"onDemandWaysTintedText",
|
|
||||||
"onDemandWaysDiscordSaturation",
|
|
||||||
"onDemandWaysOsAccentColor"
|
|
||||||
]);
|
|
||||||
if (activeColorwayObject.id === color.name && activeColorwayObject.source === color.source) {
|
|
||||||
if (isManager) {
|
|
||||||
sendColorway(nullColorwayObj);
|
|
||||||
} else {
|
|
||||||
DataStore.set("activeColorwayObject", nullColorwayObj);
|
|
||||||
setActiveColorwayObject(nullColorwayObj);
|
|
||||||
ColorwayCSS.remove();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isManager) {
|
|
||||||
const newObj: ColorwayObject = {
|
|
||||||
id: color.name,
|
|
||||||
sourceType: color.type,
|
|
||||||
source: color.source,
|
|
||||||
colors: color.colorObj
|
|
||||||
};
|
|
||||||
sendColorway(newObj);
|
|
||||||
} else {
|
|
||||||
if (onDemandWays) {
|
|
||||||
const demandedColorway = !color.isGradient ? generateCss(
|
|
||||||
colorToHex(color.primary),
|
|
||||||
colorToHex(color.secondary),
|
|
||||||
colorToHex(color.tertiary),
|
|
||||||
colorToHex(onDemandWaysOsAccentColor ? getComputedStyle(document.body).getPropertyValue("--os-accent-color") : color.accent).slice(0, 6),
|
|
||||||
onDemandWaysTintedText,
|
|
||||||
onDemandWaysDiscordSaturation,
|
|
||||||
undefined,
|
|
||||||
color.name
|
|
||||||
) : gradientBase(colorToHex(onDemandWaysOsAccentColor ? getComputedStyle(document.body).getPropertyValue("--os-accent-color") : color.accent), onDemandWaysDiscordSaturation) + `:root:root {--custom-theme-background: linear-gradient(${color.linearGradient})}`;
|
|
||||||
ColorwayCSS.set(demandedColorway);
|
|
||||||
const newObj: ColorwayObject = {
|
|
||||||
id: color.name,
|
|
||||||
css: demandedColorway,
|
|
||||||
sourceType: color.type,
|
|
||||||
source: color.source,
|
|
||||||
colors: { ...color.colorObj, accent: colorToHex(onDemandWaysOsAccentColor ? getComputedStyle(document.body).getPropertyValue("--os-accent-color") : color.accent).slice(0, 6) }
|
|
||||||
};
|
|
||||||
setActiveColorwayObject(newObj);
|
|
||||||
DataStore.set("activeColorwayObject", newObj);
|
|
||||||
} else {
|
|
||||||
ColorwayCSS.set(color["dc-import"]);
|
|
||||||
const newObj: ColorwayObject = {
|
|
||||||
id: color.name,
|
|
||||||
css: color["dc-import"],
|
|
||||||
sourceType: color.type,
|
|
||||||
source: color.source,
|
|
||||||
colors: color.colorObj
|
|
||||||
};
|
|
||||||
setActiveColorwayObject(newObj);
|
|
||||||
DataStore.set("activeColorwayObject", newObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (settings.selectorType === "multiple-selection") {
|
|
||||||
if (selectedColorways.includes(color)) {
|
|
||||||
setSelectedColorways(selectedColorways.filter(c => c !== color));
|
|
||||||
} else {
|
|
||||||
setSelectedColorways([...selectedColorways, color]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="discordColorwayPreviewColorContainer">
|
|
||||||
{!color.isGradient ? Object.values(color.colorObj as { accent?: string, primary?: string, secondary?: string, tertiary?: string; }).map(colorStr => <div
|
|
||||||
className="discordColorwayPreviewColor"
|
|
||||||
style={{
|
|
||||||
backgroundColor: `#${colorToHex(colorStr)}`,
|
|
||||||
}}
|
|
||||||
/>) : <div
|
|
||||||
className="discordColorwayPreviewColor"
|
|
||||||
style={{
|
|
||||||
background: `linear-gradient(${color.linearGradient})`,
|
|
||||||
}}
|
|
||||||
/>}
|
|
||||||
</div>
|
|
||||||
<span className="colorwayLabel">{color.name}</span>
|
|
||||||
{settings.selectorType === "normal" && <button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
openModal(props => <InfoModal
|
|
||||||
modalProps={props}
|
|
||||||
colorway={color}
|
|
||||||
loadUIProps={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>
|
|
||||||
</button>}
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
navigator.clipboard.writeText(color["dc-import"]);
|
|
||||||
Toasts.show({
|
|
||||||
message: "Copied Colorway CSS Successfully",
|
|
||||||
type: 1,
|
|
||||||
id: "copy-colorway-css-notify",
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CodeIcon width={20} height={20} />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
const colorwayIDArray = `${color.accent},${color.primary},${color.secondary},${color.tertiary}|n:${color.name}${color.preset ? `|p:${color.preset}` : ""}`;
|
|
||||||
const colorwayID = stringToHex(colorwayIDArray);
|
|
||||||
navigator.clipboard.writeText(colorwayID);
|
|
||||||
Toasts.show({
|
|
||||||
message: "Copied Colorway ID Successfully",
|
|
||||||
type: 1,
|
|
||||||
id: "copy-colorway-id-notify",
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IDIcon width={20} height={20} />
|
|
||||||
</button>
|
|
||||||
{(color.sourceType === "offline" && settings.selectorType !== "preview") && <button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
const oldStores = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).filter(sourcee => sourcee.name !== color.source);
|
|
||||||
const storeToModify = (await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]).filter(sourcee => sourcee.name === color.source)[0];
|
|
||||||
const newStore = { name: storeToModify.name, colorways: storeToModify.colorways.filter(colorway => colorway.name !== color.name) };
|
|
||||||
DataStore.set("customColorways", [...oldStores, newStore]);
|
|
||||||
setCustomColorwayData([...oldStores, newStore].map((colorSrc: { name: string, colorways: Colorway[], id?: string; }) =>
|
|
||||||
({ type: "offline", source: colorSrc.name, colorways: colorSrc.colorways })));
|
|
||||||
if ((await DataStore.get("activeColorwayObject")).id === color.name) {
|
|
||||||
DataStore.set("activeColorwayObject", nullColorwayObj);
|
|
||||||
setActiveColorwayObject(nullColorwayObj);
|
|
||||||
ColorwayCSS.remove();
|
|
||||||
}
|
|
||||||
updateRemoteSources();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DeleteIcon width={20} height={20} />
|
|
||||||
</button>}
|
|
||||||
</div> : <></>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
</>}</>;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ReactNode } from "../";
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
children,
|
|
||||||
divider = false,
|
|
||||||
disabled = false
|
|
||||||
}: { children: ReactNode, divider?: boolean, disabled?: boolean; }) {
|
|
||||||
return <div style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
marginBottom: "20px"
|
|
||||||
}}>
|
|
||||||
{disabled ? <div style={{
|
|
||||||
pointerEvents: "none",
|
|
||||||
opacity: .5,
|
|
||||||
cursor: "not-allowed"
|
|
||||||
}}>{children}</div> : children}
|
|
||||||
{divider && <div className="colorwaysSettingsDivider" />}
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, ReactNode, useCallback, useEffect, useState } from "../../";
|
|
||||||
import Setting from "../Setting";
|
|
||||||
import Switch from "../Switch";
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
hasTheme = false
|
|
||||||
}: {
|
|
||||||
hasTheme: boolean;
|
|
||||||
}) {
|
|
||||||
const [onDemand, setOnDemand] = useState<boolean>(false);
|
|
||||||
const [onDemandTinted, setOnDemandTinted] = useState<boolean>(false);
|
|
||||||
const [onDemandDiscordSat, setOnDemandDiscordSat] = useState<boolean>(false);
|
|
||||||
const [onDemandOsAccent, setOnDemandOsAccent] = useState<boolean>(false);
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
async function loadUI() {
|
|
||||||
const [
|
|
||||||
onDemandWays,
|
|
||||||
onDemandWaysTintedText,
|
|
||||||
onDemandWaysDiscordSaturation,
|
|
||||||
onDemandWaysOsAccentColor
|
|
||||||
] = await DataStore.getMany([
|
|
||||||
"onDemandWays",
|
|
||||||
"onDemandWaysTintedText",
|
|
||||||
"onDemandWaysDiscordSaturation",
|
|
||||||
"onDemandWaysOsAccentColor"
|
|
||||||
]);
|
|
||||||
setOnDemand(onDemandWays);
|
|
||||||
setOnDemandTinted(onDemandWaysTintedText);
|
|
||||||
setOnDemandDiscordSat(onDemandWaysDiscordSaturation);
|
|
||||||
if (getComputedStyle(document.body).getPropertyValue("--os-accent-color") !== "") {
|
|
||||||
setOnDemandOsAccent(onDemandWaysOsAccentColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cached_loadUI = useCallback(loadUI, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
cached_loadUI();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function Container({ children }: { children: ReactNode; }) {
|
|
||||||
if (hasTheme) return <div className="colorwaysModalTab" data-theme={theme}>{children}</div>;
|
|
||||||
else return <div className="colorwaysModalTab">{children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Container>
|
|
||||||
<Setting divider>
|
|
||||||
<Switch
|
|
||||||
label="Enable Colorways On Demand"
|
|
||||||
id="onDemandWays"
|
|
||||||
value={onDemand}
|
|
||||||
onChange={(v: boolean) => {
|
|
||||||
setOnDemand(v);
|
|
||||||
DataStore.set("onDemandWays", v);
|
|
||||||
}} />
|
|
||||||
<span className="colorwaysNote">Always utilise the latest of what DiscordColorways has to offer. CSS is being directly generated on the device and gets applied in the place of the normal import/CSS given by the colorway.</span>
|
|
||||||
</Setting>
|
|
||||||
<Setting divider disabled={!onDemand}>
|
|
||||||
<Switch
|
|
||||||
label="Use tinted text"
|
|
||||||
id="onDemandWaysTintedText"
|
|
||||||
value={onDemandTinted}
|
|
||||||
onChange={(v: boolean) => {
|
|
||||||
setOnDemandTinted(v);
|
|
||||||
DataStore.set("onDemandWaysTintedText", v);
|
|
||||||
}} />
|
|
||||||
</Setting>
|
|
||||||
<Setting divider disabled={!onDemand}>
|
|
||||||
<Switch
|
|
||||||
label="Use Discord's saturation"
|
|
||||||
id="onDemandWaysDiscordSaturation"
|
|
||||||
value={onDemandDiscordSat}
|
|
||||||
onChange={(v: boolean) => {
|
|
||||||
setOnDemandDiscordSat(v);
|
|
||||||
DataStore.set("onDemandWaysDiscordSaturation", v);
|
|
||||||
}} />
|
|
||||||
</Setting>
|
|
||||||
<Setting disabled={!onDemand || !getComputedStyle(document.body).getPropertyValue("--os-accent-color")}>
|
|
||||||
<Switch
|
|
||||||
label="Use Operating System's Accent Color"
|
|
||||||
id="onDemandWaysOsAccentColor"
|
|
||||||
value={onDemandOsAccent}
|
|
||||||
onChange={(v: boolean) => {
|
|
||||||
setOnDemandOsAccent(v);
|
|
||||||
DataStore.set("onDemandWaysOsAccentColor", v);
|
|
||||||
}} />
|
|
||||||
</Setting>
|
|
||||||
</Container>;
|
|
||||||
}
|
|
|
@ -1,280 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, FluxDispatcher, FluxEvents, PluginProps, ReactNode, useEffect, useState } from "../../";
|
|
||||||
import { defaultColorwaySource, fallbackColorways, nullColorwayObj } from "../../constants";
|
|
||||||
import { Colorway } from "../../types";
|
|
||||||
import { connect, updateShouldAutoconnect } from "../../wsClient";
|
|
||||||
import { changeThemeIDCard } from "../ColorwayID";
|
|
||||||
import { changeTheme as changeThemeMain } from "../MainModal";
|
|
||||||
import Setting from "../Setting";
|
|
||||||
import Switch from "../Switch";
|
|
||||||
|
|
||||||
function changeTheme(theme: string) {
|
|
||||||
changeThemeMain(theme);
|
|
||||||
changeThemeIDCard(theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
hasTheme = false
|
|
||||||
}: {
|
|
||||||
hasTheme?: boolean;
|
|
||||||
}) {
|
|
||||||
const [colorways, setColorways] = useState<Colorway[]>([]);
|
|
||||||
const [customColorways, setCustomColorways] = useState<Colorway[]>([]);
|
|
||||||
const [colorsButtonVisibility, setColorsButtonVisibility] = useState<boolean>(false);
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
const [shouldAutoconnect, setShouldAutoconnect] = useState<"1" | "2">("1");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
setShouldAutoconnect(await DataStore.get("colorwaysManagerDoAutoconnect") as "1" | "2");
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
(async function () {
|
|
||||||
const [
|
|
||||||
customColorways,
|
|
||||||
colorwaySourceFiles,
|
|
||||||
showColorwaysButton
|
|
||||||
] = await DataStore.getMany([
|
|
||||||
"customColorways",
|
|
||||||
"colorwaySourceFiles",
|
|
||||||
"showColorwaysButton"
|
|
||||||
]);
|
|
||||||
const responses: Response[] = await Promise.all(
|
|
||||||
colorwaySourceFiles.map(({ url }: { url: string; }) =>
|
|
||||||
fetch(url)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const data = await Promise.all(
|
|
||||||
responses.map((res: Response) =>
|
|
||||||
res.json().catch(() => { return { colorways: [] }; })
|
|
||||||
));
|
|
||||||
const colorways = data.flatMap(json => json.colorways);
|
|
||||||
setColorways(colorways || fallbackColorways);
|
|
||||||
setCustomColorways(customColorways.map(source => source.colorways).flat(2));
|
|
||||||
setColorsButtonVisibility(showColorwaysButton);
|
|
||||||
})();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function Container({ children }: { children: ReactNode; }) {
|
|
||||||
if (hasTheme) return <div className="colorwaysModalTab" data-theme={theme}>{children}</div>;
|
|
||||||
else return <div className="colorwaysModalTab">{children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Container>
|
|
||||||
<span className="colorwaysModalSectionHeader">Quick Switch</span>
|
|
||||||
<Setting divider>
|
|
||||||
<Switch
|
|
||||||
value={colorsButtonVisibility}
|
|
||||||
label="Enable Quick Switch"
|
|
||||||
id="showColorwaysButton"
|
|
||||||
onChange={(v: boolean) => {
|
|
||||||
setColorsButtonVisibility(v);
|
|
||||||
DataStore.set("showColorwaysButton", v);
|
|
||||||
FluxDispatcher.dispatch({
|
|
||||||
type: "COLORWAYS_UPDATE_BUTTON_VISIBILITY" as FluxEvents,
|
|
||||||
isVisible: v
|
|
||||||
});
|
|
||||||
}} />
|
|
||||||
<span className="colorwaysNote">Shows a button on the top of the servers list that opens a colorway selector modal.</span>
|
|
||||||
</Setting>
|
|
||||||
<span className="colorwaysModalSectionHeader">Appearance</span>
|
|
||||||
<Setting divider>
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
width: "100%",
|
|
||||||
alignItems: "center",
|
|
||||||
cursor: "pointer"
|
|
||||||
}}>
|
|
||||||
<label className="colorwaySwitch-label">Plugin Theme</label>
|
|
||||||
<select
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ border: "none" }}
|
|
||||||
onChange={e => {
|
|
||||||
setTheme(e.currentTarget.value);
|
|
||||||
DataStore.set("colorwaysPluginTheme", e.currentTarget.value);
|
|
||||||
changeTheme(e.currentTarget.value);
|
|
||||||
}}
|
|
||||||
value={theme}
|
|
||||||
>
|
|
||||||
<option value="discord">Discord (Default)</option>
|
|
||||||
<option value="colorish">Colorish</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</Setting>
|
|
||||||
<span className="colorwaysModalSectionHeader">Manager</span>
|
|
||||||
<Setting>
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
width: "100%",
|
|
||||||
alignItems: "center",
|
|
||||||
cursor: "pointer"
|
|
||||||
}}>
|
|
||||||
<label className="colorwaySwitch-label">Automatically retry to connect to Manager</label>
|
|
||||||
<select
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ border: "none" }}
|
|
||||||
onChange={({ currentTarget: { value } }) => {
|
|
||||||
if (value === "1") {
|
|
||||||
DataStore.set("colorwaysManagerDoAutoconnect", true);
|
|
||||||
updateShouldAutoconnect(true);
|
|
||||||
} else {
|
|
||||||
DataStore.set("colorwaysManagerDoAutoconnect", false);
|
|
||||||
updateShouldAutoconnect(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={shouldAutoconnect}
|
|
||||||
>
|
|
||||||
<option value="1">On (Default)</option>
|
|
||||||
<option value="2">Off</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</Setting>
|
|
||||||
<Setting divider>
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
width: "100%",
|
|
||||||
alignItems: "center",
|
|
||||||
cursor: "pointer"
|
|
||||||
}}>
|
|
||||||
<label className="colorwaySwitch-label">Try to connect to Manager manually</label>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => connect()}
|
|
||||||
value={shouldAutoconnect}
|
|
||||||
>
|
|
||||||
Try to connect...
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Setting>
|
|
||||||
<Setting divider>
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
width: "100%",
|
|
||||||
alignItems: "center",
|
|
||||||
cursor: "pointer"
|
|
||||||
}}>
|
|
||||||
<label className="colorwaySwitch-label">Reset plugin to default settings (CANNOT BE UNDONE)</label>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => {
|
|
||||||
DataStore.setMany([
|
|
||||||
["customColorways", []],
|
|
||||||
["colorwaySourceFiles", [{
|
|
||||||
name: "Project Colorway",
|
|
||||||
url: defaultColorwaySource
|
|
||||||
}]],
|
|
||||||
["showColorwaysButton", false],
|
|
||||||
["onDemandWays", false],
|
|
||||||
["onDemandWaysTintedText", true],
|
|
||||||
["onDemandWaysDiscordSaturation", false],
|
|
||||||
["onDemandWaysOsAccentColor", false],
|
|
||||||
["activeColorwayObject", nullColorwayObj],
|
|
||||||
["colorwaysPluginTheme", "discord"],
|
|
||||||
["colorwaysBoundManagers", []],
|
|
||||||
["colorwaysManagerAutoconnectPeriod", 3000],
|
|
||||||
["colorwaysManagerDoAutoconnect", true]
|
|
||||||
]);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Reset...
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<span className="colorwaysNote">Reset the plugin to its default settings. All bound managers, sources, and colorways will be deleted. Please reload Discord after use.</span>
|
|
||||||
</Setting>
|
|
||||||
<div style={{ flexDirection: "column", display: "flex" }}>
|
|
||||||
<h1 style={{
|
|
||||||
fontFamily: "var(--font-headline)",
|
|
||||||
fontSize: "24px",
|
|
||||||
color: "var(--header-primary)",
|
|
||||||
lineHeight: "31px",
|
|
||||||
marginBottom: "0"
|
|
||||||
}}>
|
|
||||||
Discord <span style={{
|
|
||||||
fontFamily: "var(--font-display)",
|
|
||||||
fontSize: "24px",
|
|
||||||
backgroundColor: "var(--brand-500)",
|
|
||||||
padding: "0 4px",
|
|
||||||
borderRadius: "4px"
|
|
||||||
}}>Colorways</span>
|
|
||||||
</h1>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color: "var(--text-normal)",
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "14px",
|
|
||||||
marginBottom: "12px"
|
|
||||||
}}
|
|
||||||
>by Project Colorway</span>
|
|
||||||
<span className="colorwaysModalSectionHeader">
|
|
||||||
Plugin Version:
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color: "var(--text-muted)",
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "14px",
|
|
||||||
marginBottom: "8px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{PluginProps.pluginVersion} ({PluginProps.clientMod})
|
|
||||||
</span>
|
|
||||||
<span className="colorwaysModalSectionHeader">
|
|
||||||
UI Version:
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color: "var(--text-muted)",
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "14px",
|
|
||||||
marginBottom: "8px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{PluginProps.UIVersion}
|
|
||||||
</span>
|
|
||||||
<span className="colorwaysModalSectionHeader">
|
|
||||||
Creator Version:
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color: "var(--text-muted)",
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "14px",
|
|
||||||
marginBottom: "8px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{PluginProps.creatorVersion}
|
|
||||||
</span>
|
|
||||||
<span className="colorwaysModalSectionHeader">
|
|
||||||
Loaded Colorways:
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color: "var(--text-muted)",
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "14px",
|
|
||||||
marginBottom: "8px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{[...colorways, ...customColorways].length}
|
|
||||||
</span>
|
|
||||||
<span className="colorwaysModalSectionHeader">
|
|
||||||
Project Repositories:
|
|
||||||
</span>
|
|
||||||
<a role="link" target="_blank" href="https://github.com/DaBluLite/DiscordColorways">DiscordColorways</a>
|
|
||||||
<a role="link" target="_blank" href="https://github.com/DaBluLite/ProjectColorway">Project Colorway</a>
|
|
||||||
</div>
|
|
||||||
</Container>;
|
|
||||||
}
|
|
|
@ -1,415 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, openModal, ReactNode, useEffect, useState } from "../../";
|
|
||||||
import { defaultColorwaySource } from "../../constants";
|
|
||||||
import { Colorway, ModalProps } from "../../types";
|
|
||||||
import { chooseFile, saveFile } from "../../utils";
|
|
||||||
import { updateRemoteSources } from "../../wsClient";
|
|
||||||
import { CopyIcon, DeleteIcon, DownloadIcon, ImportIcon, PlusIcon } from "../Icons";
|
|
||||||
import TabBar from "../TabBar";
|
|
||||||
|
|
||||||
export function StoreNameModal({ modalProps, originalName, onFinish, conflicting }: { modalProps: ModalProps, originalName: string, onFinish: (newName: string) => Promise<void>, conflicting: boolean; }) {
|
|
||||||
const [error, setError] = useState<string>("");
|
|
||||||
const [newStoreName, setNewStoreName] = useState<string>(originalName);
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
{conflicting ? "Duplicate Store Name" : "Give this store a name"}
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
{conflicting ? <span className="colorwaysModalSectionHeader">A store with the same name already exists. Please give a different name to the imported store:</span> : <></>}
|
|
||||||
<span className="colorwaysModalSectionHeader">Name:</span>
|
|
||||||
<input type="text" className="colorwaySelector-search" value={newStoreName} onChange={({ currentTarget: { value } }) => setNewStoreName(value)} style={{ marginBottom: "16px" }} />
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
style={{ marginLeft: 8 }}
|
|
||||||
onClick={async () => {
|
|
||||||
setError("");
|
|
||||||
if ((await DataStore.get("customColorways")).map(store => store.name).includes(newStoreName)) {
|
|
||||||
return setError("Error: Store name already exists");
|
|
||||||
}
|
|
||||||
onFinish(newStoreName);
|
|
||||||
modalProps.onClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ marginLeft: 8 }}
|
|
||||||
onClick={() => modalProps.onClose()}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function AddOnlineStoreModal({ modalProps, onFinish }: { modalProps: ModalProps, onFinish: (name: string, url: string) => void; }) {
|
|
||||||
const [colorwaySourceName, setColorwaySourceName] = useState<string>("");
|
|
||||||
const [colorwaySourceURL, setColorwaySourceURL] = useState<string>("");
|
|
||||||
const [nameError, setNameError] = useState<string>("");
|
|
||||||
const [URLError, setURLError] = useState<string>("");
|
|
||||||
const [nameReadOnly, setNameReadOnly] = useState<boolean>(false);
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Add a source:
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<span className="colorwaysModalSectionHeader">Name:</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
placeholder="Enter a valid Name..."
|
|
||||||
onInput={e => setColorwaySourceName(e.currentTarget.value)}
|
|
||||||
value={colorwaySourceName}
|
|
||||||
readOnly={nameReadOnly}
|
|
||||||
disabled={nameReadOnly}
|
|
||||||
/>
|
|
||||||
<span className="colorwaysModalSectionHeader" style={{ marginTop: "8px" }}>URL:</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
placeholder="Enter a valid URL..."
|
|
||||||
onChange={({ currentTarget: { value } }) => {
|
|
||||||
setColorwaySourceURL(value);
|
|
||||||
if (value === defaultColorwaySource) {
|
|
||||||
setNameReadOnly(true);
|
|
||||||
setColorwaySourceName("Project Colorway");
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={colorwaySourceURL}
|
|
||||||
style={{ marginBottom: "16px" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
const sourcesArr: { name: string, url: string; }[] = (await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]);
|
|
||||||
if (!colorwaySourceName) {
|
|
||||||
setNameError("Error: Please enter a valid name");
|
|
||||||
}
|
|
||||||
else if (!colorwaySourceURL) {
|
|
||||||
setURLError("Error: Please enter a valid URL");
|
|
||||||
}
|
|
||||||
else if (sourcesArr.map(s => s.name).includes(colorwaySourceName)) {
|
|
||||||
setNameError("Error: An online source with that name already exists");
|
|
||||||
}
|
|
||||||
else if (sourcesArr.map(s => s.url).includes(colorwaySourceURL)) {
|
|
||||||
setURLError("Error: An online source with that url already exists");
|
|
||||||
} else {
|
|
||||||
onFinish(colorwaySourceName, colorwaySourceURL);
|
|
||||||
modalProps.onClose();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => modalProps.onClose()}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
hasTheme = false
|
|
||||||
}: {
|
|
||||||
hasTheme?: boolean;
|
|
||||||
}) {
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function Container({ children }: { children: ReactNode; }) {
|
|
||||||
if (hasTheme) return <div className="colorwaysModalTab" data-theme={theme}>{children}</div>;
|
|
||||||
else return <div className="colorwaysModalTab">{children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Container>
|
|
||||||
<TabBar items={[
|
|
||||||
{
|
|
||||||
name: "Online",
|
|
||||||
component: OnlineTab
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Offline",
|
|
||||||
component: OfflineTab
|
|
||||||
}
|
|
||||||
]} />
|
|
||||||
</Container >;
|
|
||||||
}
|
|
||||||
|
|
||||||
function OfflineTab() {
|
|
||||||
const [customColorwayStores, setCustomColorwayStores] = useState<{ name: string, colorways: Colorway[]; }[]>([]);
|
|
||||||
useEffect(() => {
|
|
||||||
(async function () {
|
|
||||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
|
||||||
updateRemoteSources();
|
|
||||||
})();
|
|
||||||
}, []);
|
|
||||||
return <div className="colorwaySourceTab">
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
gap: "8px"
|
|
||||||
}}>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ flexShrink: "0" }}
|
|
||||||
onClick={async () => {
|
|
||||||
const file = await chooseFile("application/json");
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = async () => {
|
|
||||||
try {
|
|
||||||
if ((await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]).map(store => store.name).includes(JSON.parse(reader.result as string).name)) {
|
|
||||||
openModal(props => <StoreNameModal conflicting modalProps={props} originalName={JSON.parse(reader.result as string).name} onFinish={async e => {
|
|
||||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: JSON.parse(reader.result as string).colorways }]);
|
|
||||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
|
||||||
updateRemoteSources();
|
|
||||||
}} />);
|
|
||||||
} else {
|
|
||||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), JSON.parse(reader.result as string)]);
|
|
||||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
|
||||||
updateRemoteSources();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("DiscordColorways: " + err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
reader.readAsText(file);
|
|
||||||
updateRemoteSources();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ImportIcon width={14} height={14} />
|
|
||||||
Import...
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ flexShrink: "0" }}
|
|
||||||
onClick={() => {
|
|
||||||
openModal(props => <StoreNameModal conflicting={false} modalProps={props} originalName="" onFinish={async e => {
|
|
||||||
await DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: [] }]);
|
|
||||||
setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]);
|
|
||||||
props.onClose();
|
|
||||||
updateRemoteSources();
|
|
||||||
}} />);
|
|
||||||
}}>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
aria-hidden="true"
|
|
||||||
role="img"
|
|
||||||
width="14"
|
|
||||||
height="14"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
New...
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysSettings-sourceScroller">
|
|
||||||
{getComputedStyle(document.body).getPropertyValue("--os-accent-color") ? <div className={"colorwaysSettings-colorwaySource"} style={{ flexDirection: "column", padding: "16px", alignItems: "start" }}>
|
|
||||||
<div style={{ alignItems: "center", width: "100%", height: "30px", display: "flex" }}>
|
|
||||||
<span className="colorwaysSettings-colorwaySourceLabel">OS Accent Color{" "}
|
|
||||||
<div className="colorways-badge">Built-In</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div> : <></>}
|
|
||||||
{customColorwayStores.map(({ name: customColorwaySourceName, colorways: offlineStoreColorways }) => <div className={"colorwaysSettings-colorwaySource"} style={{ flexDirection: "column", padding: "16px", alignItems: "start" }}>
|
|
||||||
<span className="colorwaysSettings-colorwaySourceLabel">
|
|
||||||
{customColorwaySourceName}
|
|
||||||
</span>
|
|
||||||
<div style={{ marginLeft: "auto", gap: "8px", display: "flex" }}>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
saveFile(new File([JSON.stringify({ "name": customColorwaySourceName, "colorways": [...offlineStoreColorways] })], `${customColorwaySourceName.replaceAll(" ", "-").toLowerCase()}.colorways.json`, { type: "application/json" }));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DownloadIcon width={14} height={14} /> Export as...
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
var sourcesArr: { name: string, colorways: Colorway[]; }[] = [];
|
|
||||||
const customColorwaySources = await DataStore.get("customColorways");
|
|
||||||
customColorwaySources.map((source: { name: string, colorways: Colorway[]; }) => {
|
|
||||||
if (source.name !== customColorwaySourceName) {
|
|
||||||
sourcesArr.push(source);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
DataStore.set("customColorways", sourcesArr);
|
|
||||||
setCustomColorwayStores(sourcesArr);
|
|
||||||
updateRemoteSources();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DeleteIcon width={20} height={20} /> Remove
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function OnlineTab() {
|
|
||||||
const [colorwaySourceFiles, setColorwaySourceFiles] = useState<{ name: string, url: string; }[]>([]);
|
|
||||||
useEffect(() => {
|
|
||||||
(async function () {
|
|
||||||
setColorwaySourceFiles(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]);
|
|
||||||
updateRemoteSources();
|
|
||||||
})();
|
|
||||||
}, []);
|
|
||||||
return <div className="colorwaySourceTab">
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
gap: "8px"
|
|
||||||
}}>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ flexShrink: "0" }}
|
|
||||||
onClick={() => {
|
|
||||||
openModal(props => <AddOnlineStoreModal modalProps={props} onFinish={async (name, url) => {
|
|
||||||
await DataStore.set("colorwaySourceFiles", [...await DataStore.get("colorwaySourceFiles"), { name: name, url: url }]);
|
|
||||||
setColorwaySourceFiles([...await DataStore.get("colorwaySourceFiles"), { name: name, url: url }]);
|
|
||||||
updateRemoteSources();
|
|
||||||
}} />);
|
|
||||||
}}>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
aria-hidden="true"
|
|
||||||
role="img"
|
|
||||||
width="14"
|
|
||||||
height="14"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Add...
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysSettings-sourceScroller">
|
|
||||||
{!colorwaySourceFiles.length && <div className={"colorwaysSettings-colorwaySource"} style={{ flexDirection: "column", padding: "16px", alignItems: "start" }} onClick={async () => {
|
|
||||||
DataStore.set("colorwaySourceFiles", [{ name: "Project Colorway", url: defaultColorwaySource }, ...(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter(i => i.name !== "Project Colorway")]);
|
|
||||||
setColorwaySourceFiles([{ name: "Project Colorway", url: defaultColorwaySource }, ...(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter(i => i.name !== "Project Colorway")]);
|
|
||||||
}}>
|
|
||||||
<PlusIcon width={24} height={24} />
|
|
||||||
<span className="colorwaysSettings-colorwaySourceLabel">
|
|
||||||
Add Project Colorway Source
|
|
||||||
</span>
|
|
||||||
</div>}
|
|
||||||
{colorwaySourceFiles.map((colorwaySourceFile: { name: string, url: string; }, i: number) => <div className={"colorwaysSettings-colorwaySource"} style={{ flexDirection: "column", padding: "16px", alignItems: "start" }}>
|
|
||||||
<div className="hoverRoll">
|
|
||||||
<span className="colorwaysSettings-colorwaySourceLabel hoverRoll_normal">
|
|
||||||
{colorwaySourceFile.name} {colorwaySourceFile.url === defaultColorwaySource && <div className="colorways-badge">Built-In</div>} {colorwaySourceFile.url === "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json" && <div className="colorways-badge">Built-In | Outdated</div>}
|
|
||||||
</span>
|
|
||||||
<span className="colorwaysSettings-colorwaySourceLabel hoverRoll_hovered">
|
|
||||||
{colorwaySourceFile.url}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ marginLeft: "auto", gap: "8px", display: "flex" }}>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={() => { navigator.clipboard.writeText(colorwaySourceFile.url); }}
|
|
||||||
>
|
|
||||||
<CopyIcon width={14} height={14} /> Copy URL
|
|
||||||
</button>
|
|
||||||
{colorwaySourceFile.url === "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json" && <button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
DataStore.set("colorwaySourceFiles", [{ name: "Project Colorway", url: defaultColorwaySource }, ...(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter(i => i.name !== "Project Colorway")]);
|
|
||||||
setColorwaySourceFiles([{ name: "Project Colorway", url: defaultColorwaySource }, ...(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter(i => i.name !== "Project Colorway")]);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
width="14"
|
|
||||||
height="14"
|
|
||||||
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> Update source...
|
|
||||||
</button>}
|
|
||||||
{(colorwaySourceFile.url !== defaultColorwaySource && colorwaySourceFile.url !== "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json")
|
|
||||||
&& <>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
openModal(props => <StoreNameModal conflicting={false} modalProps={props} originalName={colorwaySourceFile.name || ""} onFinish={async e => {
|
|
||||||
const res = await fetch(colorwaySourceFile.url);
|
|
||||||
const data = await res.json();
|
|
||||||
DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: data.colorways || [] }]);
|
|
||||||
updateRemoteSources();
|
|
||||||
}} />);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DownloadIcon width={14} height={14} /> Download...
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
DataStore.set("colorwaySourceFiles", (await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter((src, ii) => ii !== i));
|
|
||||||
setColorwaySourceFiles((await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter((src, ii) => ii !== i));
|
|
||||||
updateRemoteSources();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DeleteIcon width={14} height={14} /> Remove
|
|
||||||
</button>
|
|
||||||
</>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,146 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, openModal, ReactNode, useEffect, useState } from "../../";
|
|
||||||
import { StoreItem } from "../../types";
|
|
||||||
import { DeleteIcon, DownloadIcon, PalleteIcon } from "../Icons";
|
|
||||||
import Selector from "../Selector";
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
hasTheme = false
|
|
||||||
}: {
|
|
||||||
hasTheme?: boolean;
|
|
||||||
}) {
|
|
||||||
const [storeObject, setStoreObject] = useState<StoreItem[]>([]);
|
|
||||||
const [colorwaySourceFiles, setColorwaySourceFiles] = useState<{ name: string, url: string; }[]>([]);
|
|
||||||
const [searchValue, setSearchValue] = useState<string>("");
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!searchValue) {
|
|
||||||
(async function () {
|
|
||||||
const res: Response = await fetch("https://dablulite.vercel.app/");
|
|
||||||
const data = await res.json();
|
|
||||||
setStoreObject(data.sources);
|
|
||||||
setColorwaySourceFiles(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]);
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function Container({ children }: { children: ReactNode; }) {
|
|
||||||
if (hasTheme) return <div className="colorwaysModalTab" data-theme={theme}>{children}</div>;
|
|
||||||
else return <div className="colorwaysModalTab">{children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Container>
|
|
||||||
<div style={{ display: "flex", marginBottom: "8px" }}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
placeholder="Search for sources..."
|
|
||||||
value={searchValue}
|
|
||||||
onChange={e => setSearchValue(e.currentTarget.value)}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
style={{ marginLeft: "8px", marginTop: "auto", marginBottom: "auto" }}
|
|
||||||
onClick={async function () {
|
|
||||||
const res: Response = await fetch("https://dablulite.vercel.app/");
|
|
||||||
const data = await res.json();
|
|
||||||
setStoreObject(data.sources);
|
|
||||||
setColorwaySourceFiles(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
width="14"
|
|
||||||
height="14"
|
|
||||||
style={{ boxSizing: "content-box", flexShrink: 0 }}
|
|
||||||
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>
|
|
||||||
Refresh
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysSettings-sourceScroller">
|
|
||||||
{storeObject.map((item: StoreItem) =>
|
|
||||||
item.name.toLowerCase().includes(searchValue.toLowerCase()) ? <div className={"colorwaysSettings-colorwaySource"} style={{ flexDirection: "column", padding: "16px", alignItems: "start" }}>
|
|
||||||
<div style={{ gap: ".5rem", display: "flex", marginBottom: "8px", flexDirection: "column" }}>
|
|
||||||
<span className="colorwaysSettings-colorwaySourceLabelHeader">
|
|
||||||
{item.name}
|
|
||||||
</span>
|
|
||||||
<span className="colorwaysSettings-colorwaySourceDesc">
|
|
||||||
{item.description}
|
|
||||||
</span>
|
|
||||||
<span className="colorwaysSettings-colorwaySourceDesc" style={{ opacity: ".8" }}>
|
|
||||||
by {item.authorGh}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ gap: "8px", alignItems: "center", width: "100%", display: "flex" }}>
|
|
||||||
<a role="link" target="_blank" href={"https://github.com/" + item.authorGh}>
|
|
||||||
<img src="/assets/6a853b4c87fce386cbfef4a2efbacb09.svg" alt="GitHub" />
|
|
||||||
</a>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
style={{ marginLeft: "auto" }}
|
|
||||||
onClick={async () => {
|
|
||||||
if (colorwaySourceFiles.map(source => source.name).includes(item.name)) {
|
|
||||||
const sourcesArr: { name: string, url: string; }[] = colorwaySourceFiles.filter(source => source.name !== item.name);
|
|
||||||
DataStore.set("colorwaySourceFiles", sourcesArr);
|
|
||||||
setColorwaySourceFiles(sourcesArr);
|
|
||||||
} else {
|
|
||||||
const sourcesArr: { name: string, url: string; }[] = [...colorwaySourceFiles, { name: item.name, url: item.url }];
|
|
||||||
DataStore.set("colorwaySourceFiles", sourcesArr);
|
|
||||||
setColorwaySourceFiles(sourcesArr);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{colorwaySourceFiles.map(source => source.name).includes(item.name) ? <><DeleteIcon width={14} height={14} /> Remove</> : <><DownloadIcon width={14} height={14} /> Add to Sources</>}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
openModal(props => <div className={`colorwaysModal ${props.transitionState === 2 ? "closing" : ""} ${props.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">
|
|
||||||
Previewing colorways for {item.name}
|
|
||||||
</h2>
|
|
||||||
<div className="colorwaysModalContent colorwaysModalContent-sourcePreview">
|
|
||||||
<Selector settings={{ selectorType: "preview", previewSource: item.url }} />
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PalleteIcon width={14} height={14} />
|
|
||||||
Preview
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div> : <></>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Container>;
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { MouseEvent } from "react";
|
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from "..";
|
|
||||||
|
|
||||||
export default function ({ source, sources, onSourceChange }: { source: { name: string, id: string; }, sources: { name: string, id: string; }[], onSourceChange: (sourceId: string) => void; }) {
|
|
||||||
const menuProps = useRef(null);
|
|
||||||
const [pos, setPos] = useState({ x: 0, y: 0 });
|
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
|
||||||
const [current, setCurrent] = useState(source);
|
|
||||||
|
|
||||||
function rightClickContextMenu(e: MouseEvent<HTMLButtonElement, MouseEvent>) {
|
|
||||||
e.stopPropagation();
|
|
||||||
window.dispatchEvent(new Event("click"));
|
|
||||||
setShowMenu(!showMenu);
|
|
||||||
setPos({
|
|
||||||
x: e.currentTarget.getBoundingClientRect().x,
|
|
||||||
y: e.currentTarget.getBoundingClientRect().y + e.currentTarget.offsetHeight + 8
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function onPageClick() {
|
|
||||||
setShowMenu(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener("click", onPageClick);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("click", onPageClick);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function onSourceChange_internal(newSort: { name: string, id: string; }) {
|
|
||||||
onSourceChange(newSort.id);
|
|
||||||
setCurrent(newSort);
|
|
||||||
setShowMenu(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>
|
|
||||||
{showMenu ? <nav className="colorwaysContextMenu" ref={menuProps} style={{
|
|
||||||
position: "fixed",
|
|
||||||
top: `${pos.y}px`,
|
|
||||||
left: `${pos.x}px`
|
|
||||||
}}>
|
|
||||||
{sources.map(({ name, id }) => {
|
|
||||||
return <button onClick={() => onSourceChange_internal({ name, id })} className="colorwaysContextMenuItm">
|
|
||||||
{name}
|
|
||||||
<svg aria-hidden="true" role="img" width="18" height="18" viewBox="0 0 24 24" style={{
|
|
||||||
marginLeft: "8px"
|
|
||||||
}}>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="currentColor" />
|
|
||||||
{source.id === id ? <circle className="colorwaysRadioSelected" cx="12" cy="12" r="5" /> : null}
|
|
||||||
</svg>
|
|
||||||
</button>;
|
|
||||||
})}
|
|
||||||
</nav> : null}
|
|
||||||
<button className="colorwaysPillButton" onClick={() => rightClickContextMenu}>Source: {current.name}</button>
|
|
||||||
</>;
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default function ({ className, style }: { className?: string, style?: any; }) {
|
|
||||||
return <div className={"colorwaysBtn-spinner" + (className ? ` ${className}` : "")} role="img" aria-label="Loading" style={style}>
|
|
||||||
<div className="colorwaysBtn-spinnerInner">
|
|
||||||
<svg className="colorwaysBtn-spinnerCircular" viewBox="25 25 50 50" fill="currentColor">
|
|
||||||
<circle className="colorwaysBtn-spinnerBeam colorwaysBtn-spinnerBeam3" cx="50" cy="50" r="20" />
|
|
||||||
<circle className="colorwaysBtn-spinnerBeam colorwaysBtn-spinnerBeam2" cx="50" cy="50" r="20" />
|
|
||||||
<circle className="colorwaysBtn-spinnerBeam" cx="50" cy="50" r="20" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
onChange,
|
|
||||||
value,
|
|
||||||
id,
|
|
||||||
label
|
|
||||||
}: {
|
|
||||||
id?: string,
|
|
||||||
value: boolean,
|
|
||||||
label?: string,
|
|
||||||
onChange: (checked: boolean) => void;
|
|
||||||
}) {
|
|
||||||
return label ? <div style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
width: "100%",
|
|
||||||
alignItems: "center",
|
|
||||||
cursor: "pointer"
|
|
||||||
}}>
|
|
||||||
<label className="colorwaySwitch-label" htmlFor={id}>{label}</label>
|
|
||||||
<div className={`colorwaysSettings-switch ${value ? "checked" : ""}`}>
|
|
||||||
<svg viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" aria-hidden="true" style={{
|
|
||||||
left: value ? "12px" : "-3px",
|
|
||||||
transition: ".2s ease",
|
|
||||||
display: "block",
|
|
||||||
position: "absolute",
|
|
||||||
width: "28px",
|
|
||||||
height: "18px",
|
|
||||||
margin: "3px"
|
|
||||||
}}>
|
|
||||||
<rect className="colorwaysSettings-switchCircle" fill="#000" x="4" y="0" height="20" width="20" rx="10" />
|
|
||||||
</svg>
|
|
||||||
<input checked={value} id={id} type="checkbox" style={{
|
|
||||||
position: "absolute",
|
|
||||||
opacity: 0,
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
cursor: "pointer",
|
|
||||||
borderRadius: "14px",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
margin: 0
|
|
||||||
}} tabIndex={0} onChange={e => {
|
|
||||||
onChange(e.currentTarget.checked);
|
|
||||||
}} />
|
|
||||||
</div>
|
|
||||||
</div> : <div className={`colorwaysSettings-switch ${value ? "checked" : ""}`}>
|
|
||||||
<svg viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" aria-hidden="true" style={{
|
|
||||||
left: value ? "12px" : "-3px",
|
|
||||||
transition: ".2s ease",
|
|
||||||
display: "block",
|
|
||||||
position: "absolute",
|
|
||||||
width: "28px",
|
|
||||||
height: "18px",
|
|
||||||
margin: "3px"
|
|
||||||
}}>
|
|
||||||
<rect className="colorwaysSettings-switchCircle" fill="#000" x="4" y="0" height="20" width="20" rx="10" />
|
|
||||||
</svg>
|
|
||||||
<input checked={value} id={id} type="checkbox" style={{
|
|
||||||
position: "absolute",
|
|
||||||
opacity: 0,
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
cursor: "pointer",
|
|
||||||
borderRadius: "14px",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
margin: 0
|
|
||||||
}} tabIndex={0} onChange={e => {
|
|
||||||
onChange(e.currentTarget.checked);
|
|
||||||
}} />
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useState } from "..";
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
items = []
|
|
||||||
}: {
|
|
||||||
items: { name: string, component: () => JSX.Element; }[];
|
|
||||||
}) {
|
|
||||||
const [active, setActive] = useState(items[0].name);
|
|
||||||
return <>
|
|
||||||
<div className="colorwaysMenuTabs">
|
|
||||||
{items.map(item => {
|
|
||||||
return <div className={`colorwaysMenuTab ${active === item.name ? "active" : ""}`} onClick={() => {
|
|
||||||
setActive(item.name);
|
|
||||||
}}>{item.name}</div>;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
{items.map(item => {
|
|
||||||
const Component = item.component;
|
|
||||||
return active === item.name ? <Component /> : null;
|
|
||||||
})}
|
|
||||||
</>;
|
|
||||||
}
|
|
|
@ -1,147 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { openModal } from "..";
|
|
||||||
import { ModalProps } from "../types";
|
|
||||||
import { HexToHSL } from "../utils";
|
|
||||||
import { CloseIcon } from "./Icons";
|
|
||||||
|
|
||||||
export default function ThemePreview({
|
|
||||||
accent,
|
|
||||||
primary,
|
|
||||||
secondary,
|
|
||||||
tertiary,
|
|
||||||
previewCSS,
|
|
||||||
modalProps,
|
|
||||||
isModal
|
|
||||||
}: {
|
|
||||||
accent: string,
|
|
||||||
primary: string,
|
|
||||||
secondary: string,
|
|
||||||
tertiary: string,
|
|
||||||
previewCSS?: string,
|
|
||||||
modalProps?: ModalProps,
|
|
||||||
isModal?: boolean;
|
|
||||||
}) {
|
|
||||||
return <>
|
|
||||||
<style>
|
|
||||||
{".colorwaysPreview-wrapper {color: var(--header-secondary); box-shadow: var(--legacy-elevation-border);}" + previewCSS}
|
|
||||||
</style>
|
|
||||||
<div
|
|
||||||
className="colorwaysPreview-wrapper"
|
|
||||||
style={{ background: `var(--dc-overlay-app-frame, ${tertiary})` }}
|
|
||||||
>
|
|
||||||
<div className="colorwaysPreview-titlebar" />
|
|
||||||
<div className="colorwaysPreview-body">
|
|
||||||
<div className="colorwayPreview-guilds">
|
|
||||||
<div className="colorwayPreview-guild">
|
|
||||||
<div
|
|
||||||
className="colorwayPreview-guildItem"
|
|
||||||
style={{ background: `var(--dc-guild-button, ${primary})` }}
|
|
||||||
onMouseEnter={e => e.currentTarget.style.background = accent}
|
|
||||||
onMouseLeave={e => e.currentTarget.style.background = `var(--dc-guild-button, ${primary})`}
|
|
||||||
onClick={() => {
|
|
||||||
if (isModal) {
|
|
||||||
modalProps?.onClose();
|
|
||||||
} else {
|
|
||||||
openModal((props: ModalProps) => <div className={`colorwaysPreview-modal ${props.transitionState === 2 ? "closing" : ""} ${props.transitionState === 4 ? "hidden" : ""}`}>
|
|
||||||
<style>
|
|
||||||
{previewCSS}
|
|
||||||
</style>
|
|
||||||
<ThemePreview accent={accent} primary={primary} secondary={secondary} tertiary={tertiary} isModal modalProps={props} />
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isModal ? <CloseIcon style={{ color: "var(--header-secondary)" }} /> : <svg
|
|
||||||
aria-hidden="true"
|
|
||||||
role="img"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M19,3H14V5h5v5h2V5A2,2,0,0,0,19,3Z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M19,19H14v2h5a2,2,0,0,0,2-2V14H19Z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M3,5v5H5V5h5V3H5A2,2,0,0,0,3,5Z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M5,14H3v5a2,2,0,0,0,2,2h5V19H5Z"
|
|
||||||
/>
|
|
||||||
</svg>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="colorwayPreview-guild">
|
|
||||||
<div className="colorwayPreview-guildSeparator" style={{ backgroundColor: primary }} />
|
|
||||||
</div>
|
|
||||||
<div className="colorwayPreview-guild">
|
|
||||||
<div
|
|
||||||
className="colorwayPreview-guildItem"
|
|
||||||
style={{ background: `var(--dc-guild-button, ${primary})` }}
|
|
||||||
onMouseEnter={e => e.currentTarget.style.background = accent}
|
|
||||||
onMouseLeave={e => e.currentTarget.style.background = `var(--dc-guild-button, ${primary})`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="colorwayPreview-guild">
|
|
||||||
<div
|
|
||||||
className="colorwayPreview-guildItem"
|
|
||||||
style={{ background: `var(--dc-guild-button, ${primary})` }}
|
|
||||||
onMouseEnter={e => e.currentTarget.style.background = accent}
|
|
||||||
onMouseLeave={e => e.currentTarget.style.background = `var(--dc-guild-button, ${primary})`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="colorwayPreview-channels" style={{ background: `var(--dc-overlay-3, ${secondary})` }}>
|
|
||||||
<div
|
|
||||||
className="colorwayPreview-userArea"
|
|
||||||
style={{
|
|
||||||
background: `var(--dc-secondary-alt, hsl(${HexToHSL(secondary)[0]} ${HexToHSL(secondary)[1]}% ${Math.max(HexToHSL(secondary)[2] - 3.6, 0)}%))`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="colorwayPreview-filler">
|
|
||||||
<div className="colorwayPreview-channel" style={{ backgroundColor: "var(--white-500)" }} />
|
|
||||||
<div className="colorwayPreview-channel" style={{ backgroundColor: "var(--primary-360)" }} />
|
|
||||||
<div className="colorwayPreview-channel" style={{ backgroundColor: "var(--primary-500)" }} />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="colorwayPreview-topShadow"
|
|
||||||
style={{
|
|
||||||
"--primary-900-hsl": `${HexToHSL(tertiary)[0]} ${HexToHSL(tertiary)[1]}% ${Math.max(HexToHSL(tertiary)[2] - (3.6 * 6), 0)}%`,
|
|
||||||
"--primary-500-hsl": `${HexToHSL(primary)[0]} ${HexToHSL(primary)[1]}% ${Math.min(HexToHSL(primary)[2] + (3.6 * 3), 100)}%`
|
|
||||||
} as React.CSSProperties}
|
|
||||||
>
|
|
||||||
<span style={{
|
|
||||||
fontWeight: 700,
|
|
||||||
color: "var(--text-normal)"
|
|
||||||
}}>
|
|
||||||
Preview
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="colorwayPreview-chat" style={{ background: `var(--dc-overlay-chat, ${primary})` }}>
|
|
||||||
<div
|
|
||||||
className="colorwayPreview-chatBox"
|
|
||||||
style={{
|
|
||||||
background: `var(--dc-overlay-3, hsl(${HexToHSL(primary)[0]} ${HexToHSL(primary)[1]}% ${Math.min(HexToHSL(primary)[2] + 3.6, 100)}%))`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="colorwayPreview-filler" />
|
|
||||||
<div
|
|
||||||
className="colorwayPreview-topShadow"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>;
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from "..";
|
|
||||||
|
|
||||||
export default function ({
|
|
||||||
children,
|
|
||||||
text,
|
|
||||||
position = "top"
|
|
||||||
}: {
|
|
||||||
children: (props: { onMouseEnter: () => void; onMouseLeave: () => void; onClick: () => void; }) => JSX.Element,
|
|
||||||
text: JSX.Element,
|
|
||||||
position?: "top" | "bottom" | "left" | "right";
|
|
||||||
}) {
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const [pos, setPos] = useState({ x: 0, y: 0 });
|
|
||||||
const btn = useRef(null);
|
|
||||||
|
|
||||||
function showTooltip() {
|
|
||||||
setPos({
|
|
||||||
x: (btn.current as unknown as HTMLElement).children[0].getBoundingClientRect().x + ((btn.current as unknown as HTMLElement).children[0] as HTMLElement).offsetWidth + 8,
|
|
||||||
y: (btn.current as unknown as HTMLElement).children[0].getBoundingClientRect().y
|
|
||||||
});
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onWindowUnfocused(e) {
|
|
||||||
e = e || window.event;
|
|
||||||
var from = e.relatedTarget || e.toElement;
|
|
||||||
if (!from || from.nodeName === "HTML") {
|
|
||||||
setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.addEventListener("mouseout", onWindowUnfocused);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("mouseout", onWindowUnfocused);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<div ref={btn} style={{
|
|
||||||
display: "contents"
|
|
||||||
}}>
|
|
||||||
{children({
|
|
||||||
onMouseEnter: () => showTooltip(),
|
|
||||||
onMouseLeave: () => setVisible(false),
|
|
||||||
onClick: () => setVisible(false)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className={`colorwaysTooltip colorwaysTooltip-${position} ${!visible ? "colorwaysTooltip-hidden" : ""}`} style={{
|
|
||||||
top: `${pos.y}px`,
|
|
||||||
left: `${pos.x}px`
|
|
||||||
}}>
|
|
||||||
<div className="colorwaysTooltipPointer" />
|
|
||||||
<div className="colorwaysTooltipContent">{text}</div>
|
|
||||||
</div>
|
|
||||||
</>;
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore, useEffect, useState } from "..";
|
|
||||||
import { ModalProps } from "../types";
|
|
||||||
import { getRepainterTheme } from "../utils";
|
|
||||||
|
|
||||||
export default function ({ modalProps, onFinish }: { modalProps: ModalProps, onFinish: ({ id, colors }: { id: string, colors: string[]; }) => void; }) {
|
|
||||||
const [colorwaySourceURL, setColorwaySourceURL] = useState<string>("");
|
|
||||||
const [URLError, setURLError] = useState<string>("");
|
|
||||||
const [theme, setTheme] = useState("discord");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function load() {
|
|
||||||
setTheme(await DataStore.get("colorwaysPluginTheme") as string);
|
|
||||||
}
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <div className={`colorwaysModal ${modalProps.transitionState === 2 ? "closing" : ""} ${modalProps.transitionState === 4 ? "hidden" : ""}`} data-theme={theme}>
|
|
||||||
<h2 className="colorwaysModalHeader">Use Repainter theme</h2>
|
|
||||||
<div className="colorwaysModalContent">
|
|
||||||
<span className="colorwaysModalSectionHeader">URL: {URLError ? <span className="colorwaysModalSectionError">{URLError}</span> : <></>}</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Enter a valid URL..."
|
|
||||||
onInput={e => {
|
|
||||||
setColorwaySourceURL(e.currentTarget.value);
|
|
||||||
}}
|
|
||||||
value={colorwaySourceURL}
|
|
||||||
className="colorwaySelector-search"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="colorwaysModalFooter">
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton colorwaysPillButton-onSurface"
|
|
||||||
onClick={async () => {
|
|
||||||
getRepainterTheme(colorwaySourceURL).then(data => {
|
|
||||||
onFinish({ id: data.id as any, colors: data.colors as any });
|
|
||||||
modalProps.onClose();
|
|
||||||
}).catch(e => setURLError("Error: " + e));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="colorwaysPillButton"
|
|
||||||
onClick={() => modalProps.onClose()}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
|
@ -1,317 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ColorwayObject } from "./types";
|
|
||||||
|
|
||||||
export const defaultColorwaySource = "https://raw.githubusercontent.com/ProjectColorway/ProjectColorway/master/index.json";
|
|
||||||
|
|
||||||
export const fallbackColorways = [
|
|
||||||
{
|
|
||||||
name: "Keyboard Purple",
|
|
||||||
original: false,
|
|
||||||
accent: "hsl(235 85.6% 64.7%)",
|
|
||||||
primary: "#222456",
|
|
||||||
secondary: "#1c1f48",
|
|
||||||
tertiary: "#080d1d",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/KeyboardPurple/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Eclipse",
|
|
||||||
original: false,
|
|
||||||
accent: "hsl(87 85.6% 64.7%)",
|
|
||||||
primary: "#000000",
|
|
||||||
secondary: "#181818",
|
|
||||||
tertiary: "#0a0a0a",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Eclipse/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Cyan",
|
|
||||||
original: false,
|
|
||||||
accent: "#009f88",
|
|
||||||
primary: "#202226",
|
|
||||||
secondary: "#1c1e21",
|
|
||||||
tertiary: "#141517",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Cyan/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Spotify",
|
|
||||||
original: false,
|
|
||||||
accent: "hsl(141 76% 48%)",
|
|
||||||
primary: "#121212",
|
|
||||||
secondary: "#090909",
|
|
||||||
tertiary: "#090909",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Spotify/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Bright n' Blue",
|
|
||||||
original: true,
|
|
||||||
accent: "hsl(234, 68%, 33%)",
|
|
||||||
primary: "#394aae",
|
|
||||||
secondary: "#29379d",
|
|
||||||
tertiary: "#1b278d",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/BrightBlue/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Still Young",
|
|
||||||
original: true,
|
|
||||||
accent: "hsl(58 85.6% 89%)",
|
|
||||||
primary: "#443a31",
|
|
||||||
secondary: "#7c3d3e",
|
|
||||||
tertiary: "#207578",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/StillYoung/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Sea",
|
|
||||||
original: true,
|
|
||||||
accent: "hsl(184, 100%, 50%)",
|
|
||||||
primary: "#07353b",
|
|
||||||
secondary: "#0b5e60",
|
|
||||||
tertiary: "#08201d",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Sea/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Lava",
|
|
||||||
original: true,
|
|
||||||
accent: "hsl(4, 80.4%, 32%)",
|
|
||||||
primary: "#401b17",
|
|
||||||
secondary: "#351917",
|
|
||||||
tertiary: "#230b0b",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Lava/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Solid Pink",
|
|
||||||
original: true,
|
|
||||||
accent: "hsl(340, 55.2%, 56.3%)",
|
|
||||||
primary: "#1e151c",
|
|
||||||
secondary: "#21181f",
|
|
||||||
tertiary: "#291e27",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/SolidPink/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Sand",
|
|
||||||
original: true,
|
|
||||||
accent: "hsl(41, 31%, 45%)",
|
|
||||||
primary: "#7f6c43",
|
|
||||||
secondary: "#665b33",
|
|
||||||
tertiary: "#5c5733",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Sand/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "AMOLED",
|
|
||||||
original: true,
|
|
||||||
accent: "hsl(235 85.6% 64.7%)",
|
|
||||||
primary: "#000000",
|
|
||||||
secondary: "#000000",
|
|
||||||
tertiary: "#000000",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Amoled/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Zorin",
|
|
||||||
original: false,
|
|
||||||
accent: "hsl(200, 89%, 86%)",
|
|
||||||
primary: "#171d20",
|
|
||||||
secondary: "#171d20",
|
|
||||||
tertiary: "#1e2529",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Zorin/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Desaturated",
|
|
||||||
original: false,
|
|
||||||
accent: "hsl(227, 58%, 65%)",
|
|
||||||
primary: "#35383d",
|
|
||||||
secondary: "#2c2f34",
|
|
||||||
tertiary: "#1e1f24",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Desaturated/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Crimson",
|
|
||||||
original: false,
|
|
||||||
accent: "hsl(0, 100%, 50%)",
|
|
||||||
primary: "#050000",
|
|
||||||
secondary: "#0a0000",
|
|
||||||
tertiary: "#0f0000",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Crimson/import.css);",
|
|
||||||
author: "Riddim_GLiTCH",
|
|
||||||
authorID: "801089753038061669",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Jupiter",
|
|
||||||
original: true,
|
|
||||||
accent: "#ffd89b",
|
|
||||||
primary: "#ffd89b",
|
|
||||||
secondary: "#19547b",
|
|
||||||
tertiary: "#1e1f22",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Jupiter/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
isGradient: true,
|
|
||||||
colors: ["accent", "primary", "secondary"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Neon Candy",
|
|
||||||
original: true,
|
|
||||||
accent: "#FC00FF",
|
|
||||||
primary: "#00DBDE",
|
|
||||||
secondary: "#00DBDE",
|
|
||||||
tertiary: "#00DBDE",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/NeonCandy/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
isGradient: true,
|
|
||||||
colors: ["accent", "primary"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Wildberry",
|
|
||||||
original: false,
|
|
||||||
accent: "#f40172",
|
|
||||||
primary: "#180029",
|
|
||||||
secondary: "#340057",
|
|
||||||
tertiary: "#4b007a",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Wildberry/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Facebook",
|
|
||||||
original: false,
|
|
||||||
accent: "#2375e1",
|
|
||||||
primary: "#18191a",
|
|
||||||
secondary: "#242526",
|
|
||||||
tertiary: "#3a3b3c",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Facebook/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Material You",
|
|
||||||
original: false,
|
|
||||||
accent: "#004977",
|
|
||||||
primary: "#1f1f1f",
|
|
||||||
secondary: "#28292a",
|
|
||||||
tertiary: "#2d2f31",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/MaterialYou/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Discord Teal",
|
|
||||||
original: false,
|
|
||||||
accent: "#175f6d",
|
|
||||||
primary: "#313338",
|
|
||||||
secondary: "#2b2d31",
|
|
||||||
tertiary: "#1e1f22",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/css-snippets/DiscordTeal/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
colors: ["accent"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "黄昏の花 (Twilight Blossom)",
|
|
||||||
original: true,
|
|
||||||
accent: "#e100ff",
|
|
||||||
primary: "#04000a",
|
|
||||||
secondary: "#0b0024",
|
|
||||||
tertiary: "#210042",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/TwilightBlossom/import.css);",
|
|
||||||
author: "Riddim_GLiTCH",
|
|
||||||
authorID: "801089753038061669",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Chai",
|
|
||||||
original: true,
|
|
||||||
accent: "#59cd51",
|
|
||||||
primary: "#1c1e15",
|
|
||||||
secondary: "#1e2118",
|
|
||||||
tertiary: "#24291e",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/Chai/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "CS1.6",
|
|
||||||
original: false,
|
|
||||||
accent: "#929a8d",
|
|
||||||
primary: "#3f4738",
|
|
||||||
secondary: "#5b6c51",
|
|
||||||
tertiary: "#4d5945",
|
|
||||||
"dc-import": "@import url(//dablulite.github.io/DiscordColorways/CS16/import.css);",
|
|
||||||
author: "DaBluLite",
|
|
||||||
authorID: "582170007505731594",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
export const knownThemeVars = {
|
|
||||||
"Cyan": {
|
|
||||||
variable: "--cyan-accent-color",
|
|
||||||
accent: "--cyan-accent-color",
|
|
||||||
primary: "--cyan-background-primary",
|
|
||||||
secondary: "--cyan-background-secondary"
|
|
||||||
},
|
|
||||||
"Virtual Boy": {
|
|
||||||
variable: "--VBaccent",
|
|
||||||
tertiary: "--VBaccent-muted",
|
|
||||||
alt: {
|
|
||||||
tertiary: "--VBaccent-dimmest"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Modular": {
|
|
||||||
variable: "--modular-hue",
|
|
||||||
accentVariables: {
|
|
||||||
h: "--modular-hue",
|
|
||||||
s: "--modular-saturation",
|
|
||||||
l: "--modular-lightness"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Solana": {
|
|
||||||
variable: "--accent-hue",
|
|
||||||
accentVariables: {
|
|
||||||
h: "--accent-hue",
|
|
||||||
s: "--accent-saturation",
|
|
||||||
l: "--accent-brightness"
|
|
||||||
},
|
|
||||||
primaryVariables: {
|
|
||||||
h: "--background-accent-hue",
|
|
||||||
s: "--background-accent-saturation",
|
|
||||||
l: "--background-accent-brightness"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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" }
|
|
||||||
];
|
|
||||||
|
|
||||||
export const nullColorwayObj: ColorwayObject = { id: null, css: null, sourceType: null, source: null };
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,117 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore } from ".";
|
|
||||||
import { defaultColorwaySource, nullColorwayObj } from "./constants";
|
|
||||||
|
|
||||||
export default async function () {
|
|
||||||
const [
|
|
||||||
customColorways,
|
|
||||||
colorwaySourceFiles,
|
|
||||||
showColorwaysButton,
|
|
||||||
onDemandWays,
|
|
||||||
onDemandWaysTintedText,
|
|
||||||
onDemandWaysDiscordSaturation,
|
|
||||||
onDemandWaysOsAccentColor,
|
|
||||||
activeColorwayObject,
|
|
||||||
colorwaysPluginTheme,
|
|
||||||
colorwaysBoundManagers,
|
|
||||||
colorwaysManagerAutoconnectPeriod,
|
|
||||||
colorwaysManagerDoAutoconnect
|
|
||||||
] = await DataStore.getMany([
|
|
||||||
"customColorways",
|
|
||||||
"colorwaySourceFiles",
|
|
||||||
"showColorwaysButton",
|
|
||||||
"onDemandWays",
|
|
||||||
"onDemandWaysTintedText",
|
|
||||||
"onDemandWaysDiscordSaturation",
|
|
||||||
"onDemandWaysOsAccentColor",
|
|
||||||
"activeColorwayObject",
|
|
||||||
"colorwaysPluginTheme",
|
|
||||||
"colorwaysBoundManagers",
|
|
||||||
"colorwaysManagerAutoconnectPeriod",
|
|
||||||
"colorwaysManagerDoAutoconnect"
|
|
||||||
]);
|
|
||||||
|
|
||||||
const defaults = [
|
|
||||||
{
|
|
||||||
name: "colorwaysManagerAutoconnectPeriod",
|
|
||||||
value: colorwaysManagerAutoconnectPeriod,
|
|
||||||
default: 3000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "colorwaysManagerDoAutoconnect",
|
|
||||||
value: colorwaysManagerDoAutoconnect,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "showColorwaysButton",
|
|
||||||
value: showColorwaysButton,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "onDemandWays",
|
|
||||||
value: onDemandWays,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "onDemandWaysTintedText",
|
|
||||||
value: onDemandWaysTintedText,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "onDemandWaysDiscordSaturation",
|
|
||||||
value: onDemandWaysDiscordSaturation,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "onDemandWaysOsAccentColor",
|
|
||||||
value: onDemandWaysOsAccentColor,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "colorwaysBoundManagers",
|
|
||||||
value: colorwaysBoundManagers,
|
|
||||||
default: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "activeColorwayObject",
|
|
||||||
value: activeColorwayObject,
|
|
||||||
default: nullColorwayObj
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "colorwaysPluginTheme",
|
|
||||||
value: colorwaysPluginTheme,
|
|
||||||
default: "discord"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
defaults.forEach(({ name, value, default: def }) => {
|
|
||||||
if (!value) DataStore.set(name, def);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (customColorways) {
|
|
||||||
if (!customColorways[0].colorways) {
|
|
||||||
DataStore.set("customColorways", [{ name: "Custom", colorways: customColorways }]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DataStore.set("customColorways", []);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colorwaySourceFiles) {
|
|
||||||
if (typeof colorwaySourceFiles[0] === "string") {
|
|
||||||
DataStore.set("colorwaySourceFiles", colorwaySourceFiles.map((sourceURL: string, i: number) => {
|
|
||||||
return { name: sourceURL === defaultColorwaySource ? "Project Colorway" : `Source #${i}`, url: sourceURL === "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json" ? defaultColorwaySource : sourceURL };
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DataStore.set("colorwaySourceFiles", [{
|
|
||||||
name: "Project Colorway",
|
|
||||||
url: defaultColorwaySource
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,267 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Plugin Imports
|
|
||||||
import * as $DataStore from "@api/DataStore";
|
|
||||||
import { addAccessory, removeAccessory } from "@api/MessageAccessories";
|
|
||||||
import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList";
|
|
||||||
import { disableStyle, enableStyle } from "@api/Styles";
|
|
||||||
import { Devs, EquicordDevs } from "@utils/constants";
|
|
||||||
import { openModal } from "@utils/modal";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
import {
|
|
||||||
i18n,
|
|
||||||
SettingsRouter
|
|
||||||
} from "@webpack/common";
|
|
||||||
import { FluxEvents as $FluxEvents } from "@webpack/types";
|
|
||||||
// Mod-specific imports
|
|
||||||
import {
|
|
||||||
CSSProperties as $CSSProperties,
|
|
||||||
ReactNode as $ReactNode
|
|
||||||
} from "react";
|
|
||||||
|
|
||||||
import { ColorwayCSS } from "./colorwaysAPI";
|
|
||||||
import ColorwayID from "./components/ColorwayID";
|
|
||||||
import ColorwaysButton from "./components/ColorwaysButton";
|
|
||||||
import CreatorModal from "./components/CreatorModal";
|
|
||||||
import PCSMigrationModal from "./components/PCSMigrationModal";
|
|
||||||
import Selector from "./components/Selector";
|
|
||||||
import OnDemandWaysPage from "./components/SettingsTabs/OnDemandPage";
|
|
||||||
import SettingsPage from "./components/SettingsTabs/SettingsPage";
|
|
||||||
import SourceManager from "./components/SettingsTabs/SourceManager";
|
|
||||||
import Store from "./components/SettingsTabs/Store";
|
|
||||||
import Spinner from "./components/Spinner";
|
|
||||||
import { defaultColorwaySource } from "./constants";
|
|
||||||
import defaultsLoader from "./defaultsLoader";
|
|
||||||
import style from "./style.css?managed";
|
|
||||||
import discordTheme from "./theme.discord.css?managed";
|
|
||||||
import { ColorPickerProps, ColorwayObject } from "./types";
|
|
||||||
import { connect } from "./wsClient";
|
|
||||||
|
|
||||||
export const DataStore = $DataStore;
|
|
||||||
export type ReactNode = $ReactNode;
|
|
||||||
export type CSSProperties = $CSSProperties;
|
|
||||||
export type FluxEvents = $FluxEvents;
|
|
||||||
export { closeModal, openModal } from "@utils/modal";
|
|
||||||
export {
|
|
||||||
Clipboard,
|
|
||||||
FluxDispatcher,
|
|
||||||
i18n,
|
|
||||||
ReactDOM,
|
|
||||||
SettingsRouter,
|
|
||||||
Slider,
|
|
||||||
Toasts,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useReducer,
|
|
||||||
useRef,
|
|
||||||
UserStore,
|
|
||||||
useState,
|
|
||||||
useStateFromStores
|
|
||||||
} from "@webpack/common";
|
|
||||||
|
|
||||||
export let ColorPicker: React.FunctionComponent<ColorPickerProps> = () => {
|
|
||||||
return <Spinner className="colorways-creator-module-warning" />;
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultsLoader();
|
|
||||||
|
|
||||||
export const PluginProps = {
|
|
||||||
pluginVersion: "6.1.0",
|
|
||||||
clientMod: "Vencord User Plugin",
|
|
||||||
UIVersion: "2.0.0",
|
|
||||||
creatorVersion: "1.20"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "DiscordColorways",
|
|
||||||
description: "A plugin that offers easy access to simple color schemes/themes for Discord, also known as Colorways",
|
|
||||||
authors: [EquicordDevs.DaBluLite, Devs.ImLvna],
|
|
||||||
dependencies: ["ServerListAPI", "MessageAccessoriesAPI"],
|
|
||||||
pluginVersion: PluginProps.pluginVersion,
|
|
||||||
toolboxActions: {
|
|
||||||
"Open Colorway Creator": () => openModal(props => <CreatorModal modalProps={props} />),
|
|
||||||
"Open Settings": () => SettingsRouter.open("ColorwaysSettings"),
|
|
||||||
},
|
|
||||||
patches: [
|
|
||||||
// Credits to Kyuuhachi for the BetterSettings plugin patches
|
|
||||||
{
|
|
||||||
find: "this.renderArtisanalHack()",
|
|
||||||
replacement: {
|
|
||||||
match: /createPromise:\(\)=>([^:}]*?),webpackId:"?\d+"?,name:(?!="CollectiblesShop")"[^"]+"/g,
|
|
||||||
replace: "$&,_:$1",
|
|
||||||
predicate: () => true
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: "Messages.USER_SETTINGS_WITH_BUILD_OVERRIDE.format",
|
|
||||||
replacement: {
|
|
||||||
match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?null!=\i&&.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/,
|
|
||||||
replace: "$&(async ()=>$2)(),"
|
|
||||||
},
|
|
||||||
predicate: () => true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: "colorPickerFooter:",
|
|
||||||
replacement: {
|
|
||||||
match: /function (\i).{0,200}colorPickerFooter:/,
|
|
||||||
replace: "$self.ColorPicker=$1;$&",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
set ColorPicker(e) {
|
|
||||||
ColorPicker = e;
|
|
||||||
},
|
|
||||||
|
|
||||||
isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) {
|
|
||||||
const firstChild = settings?.[0];
|
|
||||||
// lowest two elements... sanity backup
|
|
||||||
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true;
|
|
||||||
|
|
||||||
const settingsLocation = "belowNitro";
|
|
||||||
|
|
||||||
if (!header) return;
|
|
||||||
|
|
||||||
const names = {
|
|
||||||
top: i18n.Messages.USER_SETTINGS,
|
|
||||||
aboveNitro: i18n.Messages.BILLING_SETTINGS,
|
|
||||||
belowNitro: i18n.Messages.APP_SETTINGS,
|
|
||||||
aboveActivity: i18n.Messages.ACTIVITY_SETTINGS
|
|
||||||
};
|
|
||||||
return header === names[settingsLocation];
|
|
||||||
},
|
|
||||||
|
|
||||||
patchedSettings: new WeakSet(),
|
|
||||||
|
|
||||||
addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: Record<string, unknown>) {
|
|
||||||
if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return;
|
|
||||||
|
|
||||||
this.patchedSettings.add(elements);
|
|
||||||
|
|
||||||
elements.push(...this.makeSettingsCategories(sectionTypes));
|
|
||||||
},
|
|
||||||
|
|
||||||
makeSettingsCategories(SectionTypes: Record<string, unknown>) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
section: SectionTypes.HEADER,
|
|
||||||
label: "Discord Colorways",
|
|
||||||
className: "vc-settings-header"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
section: "ColorwaysSelector",
|
|
||||||
label: "Colorways",
|
|
||||||
element: () => <Selector hasTheme />,
|
|
||||||
className: "dc-colorway-selector"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
section: "ColorwaysSettings",
|
|
||||||
label: "Settings",
|
|
||||||
element: () => <SettingsPage hasTheme />,
|
|
||||||
className: "dc-colorway-settings"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
section: "ColorwaysSourceManager",
|
|
||||||
label: "Sources",
|
|
||||||
element: () => <SourceManager hasTheme />,
|
|
||||||
className: "dc-colorway-sources-manager"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
section: "ColorwaysOnDemand",
|
|
||||||
label: "On-Demand",
|
|
||||||
element: () => <OnDemandWaysPage hasTheme />,
|
|
||||||
className: "dc-colorway-ondemand"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
section: "ColorwaysStore",
|
|
||||||
label: "Store",
|
|
||||||
element: () => <Store hasTheme />,
|
|
||||||
className: "dc-colorway-store"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
section: SectionTypes.DIVIDER
|
|
||||||
}
|
|
||||||
].filter(Boolean);
|
|
||||||
},
|
|
||||||
|
|
||||||
ColorwaysButton: () => <ColorwaysButton />,
|
|
||||||
|
|
||||||
async start() {
|
|
||||||
const customSettingsSections = (
|
|
||||||
Vencord.Plugins.plugins.Settings as any as {
|
|
||||||
customSections: ((ID: Record<string, unknown>) => any)[];
|
|
||||||
}
|
|
||||||
).customSections;
|
|
||||||
|
|
||||||
const ColorwaysSelector = () => ({
|
|
||||||
section: "ColorwaysSelector",
|
|
||||||
label: "Colorways Selector",
|
|
||||||
element: () => <Selector hasTheme />,
|
|
||||||
className: "dc-colorway-selector"
|
|
||||||
});
|
|
||||||
const ColorwaysSettings = () => ({
|
|
||||||
section: "ColorwaysSettings",
|
|
||||||
label: "Colorways Settings",
|
|
||||||
element: () => <SettingsPage hasTheme />,
|
|
||||||
className: "dc-colorway-settings"
|
|
||||||
});
|
|
||||||
const ColorwaysSourceManager = () => ({
|
|
||||||
section: "ColorwaysSourceManager",
|
|
||||||
label: "Colorways Sources",
|
|
||||||
element: () => <SourceManager hasTheme />,
|
|
||||||
className: "dc-colorway-sources-manager"
|
|
||||||
});
|
|
||||||
const ColorwaysOnDemand = () => ({
|
|
||||||
section: "ColorwaysOnDemand",
|
|
||||||
label: "Colorways On-Demand",
|
|
||||||
element: () => <OnDemandWaysPage hasTheme />,
|
|
||||||
className: "dc-colorway-ondemand"
|
|
||||||
});
|
|
||||||
const ColorwaysStore = () => ({
|
|
||||||
section: "ColorwaysStore",
|
|
||||||
label: "Colorways Store",
|
|
||||||
element: () => <Store hasTheme />,
|
|
||||||
className: "dc-colorway-store"
|
|
||||||
});
|
|
||||||
|
|
||||||
customSettingsSections.push(ColorwaysSelector, ColorwaysSettings, ColorwaysSourceManager, ColorwaysOnDemand, ColorwaysStore);
|
|
||||||
|
|
||||||
addServerListElement(ServerListRenderPosition.Above, this.ColorwaysButton);
|
|
||||||
|
|
||||||
connect();
|
|
||||||
|
|
||||||
enableStyle(style);
|
|
||||||
enableStyle(discordTheme);
|
|
||||||
ColorwayCSS.set((await DataStore.get("activeColorwayObject") as ColorwayObject).css || "");
|
|
||||||
|
|
||||||
if ((await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).map(i => i.url).includes("https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json") || (!(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).map(i => i.url).includes("https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json") && !(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).map(i => i.url).includes("https://raw.githubusercontent.com/ProjectColorway/ProjectColorway/master/index.json"))) {
|
|
||||||
DataStore.set("colorwaySourceFiles", [{ name: "Project Colorway", url: defaultColorwaySource }, ...(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter(i => i.name !== "Project Colorway")]);
|
|
||||||
openModal(props => <PCSMigrationModal modalProps={props} />);
|
|
||||||
}
|
|
||||||
|
|
||||||
addAccessory("colorway-id-card", props => <ColorwayID props={props} />);
|
|
||||||
},
|
|
||||||
stop() {
|
|
||||||
removeServerListElement(ServerListRenderPosition.Above, this.ColorwaysButton);
|
|
||||||
disableStyle(style);
|
|
||||||
disableStyle(discordTheme);
|
|
||||||
ColorwayCSS.remove();
|
|
||||||
removeAccessory("colorway-id-card");
|
|
||||||
const customSettingsSections = (
|
|
||||||
Vencord.Plugins.plugins.Settings as any as {
|
|
||||||
customSections: ((ID: Record<string, unknown>) => any)[];
|
|
||||||
}
|
|
||||||
).customSections;
|
|
||||||
|
|
||||||
const i = customSettingsSections.findIndex(
|
|
||||||
section => section({}).id === ("ColorwaysSelector" || "ColorwaysSettings" || "ColorwaysSourceManager" || "ColorwaysOnDemand" || "ColorwaysStore")
|
|
||||||
);
|
|
||||||
|
|
||||||
if (i !== -1) customSettingsSections.splice(i, 1);
|
|
||||||
},
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,334 +0,0 @@
|
||||||
/* stylelint-disable property-no-vendor-prefix */
|
|
||||||
.colorwaySelectorModal[data-theme="discord"],
|
|
||||||
.colorwayModal[data-theme="discord"] {
|
|
||||||
border: none;
|
|
||||||
box-shadow: var(--legacy-elevation-border), var(--legacy-elevation-high);
|
|
||||||
background-color: var(--modal-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysSettingsDivider {
|
|
||||||
border-color: var(--background-modifier-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaySwitch-label,
|
|
||||||
[data-theme="discord"] .colorwaysNote {
|
|
||||||
color: var(--header-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysSettings-switchCircle {
|
|
||||||
fill: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysSettings-switch {
|
|
||||||
background-color: rgb(128 132 142);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysSettings-switch.checked {
|
|
||||||
background-color: #23a55a;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] > .colorwaySelectorSidebar > .colorwaySelectorSidebar-tab {
|
|
||||||
transition: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] > .colorwaySelectorSidebar > .colorwaySelectorSidebar-tab.active {
|
|
||||||
background-color: var(--background-modifier-selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] > .colorwaySelectorSidebar > .colorwaySelectorSidebar-tab:hover {
|
|
||||||
background-color: var(--background-modifier-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysPillButton {
|
|
||||||
color: var(--white-500);
|
|
||||||
background-color: var(--button-secondary-background);
|
|
||||||
height: var(--custom-button-button-sm-height);
|
|
||||||
min-width: var(--custom-button-button-sm-width);
|
|
||||||
min-height: var(--custom-button-button-sm-height);
|
|
||||||
width: auto;
|
|
||||||
transition:
|
|
||||||
background-color var(--custom-button-transition-duration) ease,
|
|
||||||
color var(--custom-button-transition-duration) ease;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 16px;
|
|
||||||
padding: 2px 16px;
|
|
||||||
-moz-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysPillButton:hover {
|
|
||||||
background-color: var(--button-secondary-background-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] > .colorwaySelectorSidebar {
|
|
||||||
border-top-left-radius: 4px;
|
|
||||||
border-bottom-left-radius: 4px;
|
|
||||||
background-color: var(--modal-footer-background);
|
|
||||||
box-shadow: inset 0 1px 0 hsl(var(--primary-630-hsl) / 60%);
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaySelector-search {
|
|
||||||
border-radius: 3px;
|
|
||||||
color: var(--text-normal);
|
|
||||||
background-color: var(--input-background) !important;
|
|
||||||
height: 40px;
|
|
||||||
padding: 10px;
|
|
||||||
transition: none;
|
|
||||||
font-size: 16px;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysSettings-colorwaySource {
|
|
||||||
border-radius: 4px;
|
|
||||||
color: var(--interactive-normal);
|
|
||||||
background-color: var(--background-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysSettings-colorwaySource:hover {
|
|
||||||
color: var(--interactive-active);
|
|
||||||
background-color: var(--background-modifier-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .discordColorway {
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: none;
|
|
||||||
background-color: var(--background-secondary);
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .discordColorway:hover {
|
|
||||||
filter: none;
|
|
||||||
background-color: var(--background-modifier-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .discordColorway[aria-checked="true"] {
|
|
||||||
background-color: var(--background-modifier-selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysSettings-colorwaySourceLabelHeader,
|
|
||||||
[data-theme="discord"] .colorwaysSettings-colorwaySourceDesc {
|
|
||||||
color: var(--header-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorways-badge {
|
|
||||||
height: 16px;
|
|
||||||
padding: 0 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-left: 4px;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
background: var(--bg-brand);
|
|
||||||
color: var(--white);
|
|
||||||
text-transform: uppercase;
|
|
||||||
vertical-align: top;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
text-indent: 0;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 0.625rem;
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.colorwaysModal[data-theme="discord"] {
|
|
||||||
box-shadow: var(--legacy-elevation-border), var(--legacy-elevation-high);
|
|
||||||
background-color: var(--modal-background);
|
|
||||||
border-radius: 4px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0 auto;
|
|
||||||
pointer-events: all;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysMenuTabs {
|
|
||||||
padding-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysMenuTab {
|
|
||||||
padding: 0;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
margin-right: 32px;
|
|
||||||
margin-bottom: -2px;
|
|
||||||
border-bottom: 2px solid transparent;
|
|
||||||
transition: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background-color: transparent;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysMenuTab:hover {
|
|
||||||
color: var(--interactive-hover);
|
|
||||||
border-bottom-color: var(--brand-500);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysMenuTab.active {
|
|
||||||
cursor: default;
|
|
||||||
color: var(--interactive-active);
|
|
||||||
border-bottom-color: var(--control-brand-foreground);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysModalFooter {
|
|
||||||
border-radius: 0 0 5px 5px;
|
|
||||||
background-color: var(--modal-footer-background);
|
|
||||||
padding: 16px;
|
|
||||||
box-shadow: inset 0 1px 0 hsl(var(--primary-630-hsl) / 60%);
|
|
||||||
gap: 0;
|
|
||||||
width: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton {
|
|
||||||
width: auto;
|
|
||||||
height: var(--custom-button-button-md-height);
|
|
||||||
min-width: var(--custom-button-button-md-width);
|
|
||||||
min-height: var(--custom-button-button-md-height);
|
|
||||||
transition:
|
|
||||||
color var(--custom-button-transition-duration) ease,
|
|
||||||
background-color var(--custom-button-transition-duration) ease,
|
|
||||||
border-color var(--custom-button-transition-duration) ease;
|
|
||||||
border: 1px solid var(--button-outline-primary-border);
|
|
||||||
color: var(--button-outline-primary-text);
|
|
||||||
margin-left: 8px;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton:hover {
|
|
||||||
background-color: var(--button-outline-primary-background-hover);
|
|
||||||
border-color: var(--button-outline-primary-border-hover);
|
|
||||||
color: var(--button-outline-primary-text-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton:active {
|
|
||||||
background-color: var(--button-outline-primary-background-active);
|
|
||||||
border-color: var(--button-outline-primary-border-active);
|
|
||||||
color: var(--button-outline-primary-text-active);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton.colorwaysPillButton-onSurface {
|
|
||||||
color: var(--white-500);
|
|
||||||
background-color: var(--brand-500);
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton.colorwaysPillButton-onSurface:hover {
|
|
||||||
background-color: var(--brand-560);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton.colorwaysPillButton-onSurface:active {
|
|
||||||
background-color: var(--brand-600);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysModalHeader {
|
|
||||||
box-shadow:
|
|
||||||
0 1px 0 0 hsl(var(--primary-800-hsl) / 30%),
|
|
||||||
0 1px 2px 0 hsl(var(--primary-800-hsl) / 30%);
|
|
||||||
border-radius: 4px 4px 0 0;
|
|
||||||
transition: box-shadow 0.1s ease-out;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysModalSectionHeader,
|
|
||||||
[data-theme="discord"] .colorwaysSettings-colorwaySourceLabel,
|
|
||||||
[data-theme="discord"] .colorwaysSettings-colorwaySourceLabelHeader,
|
|
||||||
[data-theme="discord"] .colorwaysSettings-colorwaySourceDesc {
|
|
||||||
color: var(--header-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysCreator-setting,
|
|
||||||
[data-theme="discord"] .colorwaysCreator-settingCat {
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: var(--background-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysCreator-setting:hover {
|
|
||||||
background-color: var(--background-modifier-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysContextMenu {
|
|
||||||
background: var(--background-floating);
|
|
||||||
box-shadow: var(--shadow-high);
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 6px 8px;
|
|
||||||
border: none;
|
|
||||||
gap: 0;
|
|
||||||
min-width: 188px;
|
|
||||||
max-width: 320px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysContextMenuItm {
|
|
||||||
border: none;
|
|
||||||
transition: none;
|
|
||||||
margin: 2px 0;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 18px;
|
|
||||||
color: var(--interactive-normal);
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysContextMenuItm:hover {
|
|
||||||
background-color: var(--menu-item-default-hover-bg);
|
|
||||||
color: var(--white);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysContextMenuItm:active {
|
|
||||||
background-color: var(--menu-item-default-active-bg);
|
|
||||||
color: var(--white);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysRadioSelected {
|
|
||||||
fill: var(--control-brand-foreground-new);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysConflictingColors-warning {
|
|
||||||
color: var(--text-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysManagerConnectionMenu {
|
|
||||||
transition:
|
|
||||||
transform 0.1s ease,
|
|
||||||
opacity 0.1s ease;
|
|
||||||
transform: scale(0.95);
|
|
||||||
transform-origin: 0% 50%;
|
|
||||||
background-color: var(--background-floating);
|
|
||||||
box-shadow: var(--shadow-high);
|
|
||||||
color: var(--text-normal);
|
|
||||||
border: none;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.colorwayIDCard[data-theme="discord"] > .colorwayMessage {
|
|
||||||
border-radius: 5px;
|
|
||||||
border: none;
|
|
||||||
background-color: var(--background-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-dark .colorwayIDCard[data-theme="discord"] .colorwayMessage {
|
|
||||||
background: hsl(var(--primary-630-hsl) / 60%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-light .colorwayIDCard[data-theme="discord"] .colorwayMessage {
|
|
||||||
background: hsl(var(--primary-100-hsl) / 60%);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysManagerConnectionValue {
|
|
||||||
color: var(--text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="discord"] .colorwaysManagerConnectionValue > b {
|
|
||||||
color: var(--text-normal);
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface Colorway {
|
|
||||||
[key: string]: any,
|
|
||||||
name: string,
|
|
||||||
"dc-import": string,
|
|
||||||
accent: string,
|
|
||||||
primary: string,
|
|
||||||
secondary: string,
|
|
||||||
tertiary: string,
|
|
||||||
original?: boolean,
|
|
||||||
author: string,
|
|
||||||
authorID: string,
|
|
||||||
colors?: string[],
|
|
||||||
isGradient?: boolean,
|
|
||||||
sourceType?: "online" | "offline" | "temporary" | null,
|
|
||||||
source?: string,
|
|
||||||
linearGradient?: string,
|
|
||||||
preset?: string,
|
|
||||||
creatorVersion: string,
|
|
||||||
colorObj?: { accent?: string, primary?: string, secondary?: string, tertiary?: string; };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ColorPickerProps {
|
|
||||||
color: number;
|
|
||||||
showEyeDropper: boolean;
|
|
||||||
suggestedColors: string[];
|
|
||||||
label: any;
|
|
||||||
onChange(color: number): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ColorwayObject {
|
|
||||||
id: string | null,
|
|
||||||
css?: string | null,
|
|
||||||
sourceType: "online" | "offline" | "temporary" | null,
|
|
||||||
source: string | null | undefined,
|
|
||||||
colors?: {
|
|
||||||
accent?: string | undefined,
|
|
||||||
primary?: string | undefined,
|
|
||||||
secondary?: string | undefined,
|
|
||||||
tertiary?: string | undefined;
|
|
||||||
} | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SourceObject {
|
|
||||||
type: "online" | "offline" | "temporary",
|
|
||||||
source: string,
|
|
||||||
colorways: Colorway[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum SortOptions {
|
|
||||||
NAME_AZ = 1,
|
|
||||||
NAME_ZA = 2,
|
|
||||||
SOURCE_AZ = 3,
|
|
||||||
SOURCE_ZA = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StoreObject {
|
|
||||||
sources: StoreItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StoreItem {
|
|
||||||
name: string,
|
|
||||||
id: string,
|
|
||||||
description: string,
|
|
||||||
url: string,
|
|
||||||
authorGh: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModalProps {
|
|
||||||
transitionState: 0 | 1 | 2 | 3 | 4;
|
|
||||||
onClose(): void;
|
|
||||||
}
|
|
|
@ -1,231 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function HexToHSL(H: string) {
|
|
||||||
let r: any = 0, g: any = 0, b: any = 0;
|
|
||||||
if (H.length === 4) r = "0x" + H[1] + H[1], g = "0x" + H[2] + H[2], b = "0x" + H[3] + H[3];
|
|
||||||
else if (H.length === 7) {
|
|
||||||
r = "0x" + H[1] + H[2];
|
|
||||||
g = "0x" + H[3] + H[4];
|
|
||||||
b = "0x" + H[5] + H[6];
|
|
||||||
}
|
|
||||||
r /= 255, g /= 255, b /= 255;
|
|
||||||
var cmin = Math.min(r, g, b),
|
|
||||||
cmax = Math.max(r, g, b),
|
|
||||||
delta = cmax - cmin,
|
|
||||||
h = 0,
|
|
||||||
s = 0,
|
|
||||||
l = 0;
|
|
||||||
if (delta === 0) h = 0;
|
|
||||||
else if (cmax === r) h = ((g - b) / delta) % 6;
|
|
||||||
else if (cmax === g) h = (b - r) / delta + 2;
|
|
||||||
else h = (r - g) / delta + 4;
|
|
||||||
h = Math.round(h * 60);
|
|
||||||
if (h < 0) h += 360;
|
|
||||||
l = (cmax + cmin) / 2;
|
|
||||||
s = delta === 0
|
|
||||||
? 0
|
|
||||||
: delta / (1 - Math.abs(2 * l - 1));
|
|
||||||
s = +(s * 100).toFixed(1);
|
|
||||||
l = +(l * 100).toFixed(1);
|
|
||||||
|
|
||||||
return [Math.round(h), Math.round(s), Math.round(l)];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const canonicalizeHex = (hex: string) => {
|
|
||||||
const canvas = document.createElement("canvas");
|
|
||||||
const ctx = canvas.getContext("2d")!;
|
|
||||||
|
|
||||||
ctx.fillStyle = hex;
|
|
||||||
hex = ctx.fillStyle;
|
|
||||||
canvas.remove();
|
|
||||||
|
|
||||||
return hex;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const stringToHex = (str: string) => {
|
|
||||||
let hex = "";
|
|
||||||
for (
|
|
||||||
let i = 0;
|
|
||||||
i < str.length;
|
|
||||||
i++
|
|
||||||
) {
|
|
||||||
const charCode = str.charCodeAt(i);
|
|
||||||
const hexValue = charCode.toString(16);
|
|
||||||
hex += hexValue.padStart(2, "0");
|
|
||||||
}
|
|
||||||
return hex;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const hexToString = (hex: string) => {
|
|
||||||
let str = "";
|
|
||||||
for (let i = 0; i < hex.length; i += 2) {
|
|
||||||
const hexValue = hex.substr(i, 2);
|
|
||||||
const decimalValue = parseInt(hexValue, 16);
|
|
||||||
str += String.fromCharCode(decimalValue);
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getHex(str: string): string {
|
|
||||||
const color = Object.assign(
|
|
||||||
document.createElement("canvas").getContext("2d") as {},
|
|
||||||
{ fillStyle: str }
|
|
||||||
).fillStyle;
|
|
||||||
if (color.includes("rgba(")) {
|
|
||||||
return getHex(String([...color.split(",").slice(0, 3), ")"]).replace(",)", ")").replace("a", ""));
|
|
||||||
} else {
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFontOnBg(bgColor: string) {
|
|
||||||
var color = (bgColor.charAt(0) === "#") ? bgColor.substring(1, 7) : bgColor;
|
|
||||||
var r = parseInt(color.substring(0, 2), 16);
|
|
||||||
var g = parseInt(color.substring(2, 4), 16);
|
|
||||||
var b = parseInt(color.substring(4, 6), 16);
|
|
||||||
return (((r * 0.299) + (g * 0.587) + (b * 0.114)) > 186) ?
|
|
||||||
"#000000" : "#ffffff";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function $e(funcArray: Array<(...vars: any) => void>, ...vars: any[]) {
|
|
||||||
funcArray.forEach(e => e(vars));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hslToHex(h: number, s: number, l: number) {
|
|
||||||
h /= 360;
|
|
||||||
s /= 100;
|
|
||||||
l /= 100;
|
|
||||||
let r: any, g: any, b: any;
|
|
||||||
if (s === 0) {
|
|
||||||
r = g = b = l; // achromatic
|
|
||||||
} else {
|
|
||||||
const hue2rgb = (p: number, q: number, t: number) => {
|
|
||||||
if (t < 0) t += 1;
|
|
||||||
if (t > 1) t -= 1;
|
|
||||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
||||||
if (t < 1 / 2) return q;
|
|
||||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
||||||
return p;
|
|
||||||
};
|
|
||||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
||||||
const p = 2 * l - q;
|
|
||||||
r = hue2rgb(p, q, h + 1 / 3);
|
|
||||||
g = hue2rgb(p, q, h);
|
|
||||||
b = hue2rgb(p, q, h - 1 / 3);
|
|
||||||
}
|
|
||||||
const toHex = (x: number) => {
|
|
||||||
const hex = Math.round(x * 255).toString(16);
|
|
||||||
return hex.length === 1 ? "0" + hex : hex;
|
|
||||||
};
|
|
||||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function rgbToHex(r: number, g: number, b: number) {
|
|
||||||
const toHex = (x: number) => {
|
|
||||||
const hex = Math.round(x * 255).toString(16);
|
|
||||||
return hex.length === 1 ? "0" + hex : hex;
|
|
||||||
};
|
|
||||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function colorToHex(color: string) {
|
|
||||||
var colorType = "hex";
|
|
||||||
if (color.includes("hsl")) {
|
|
||||||
colorType = "hsl";
|
|
||||||
} else if (color.includes("rgb")) {
|
|
||||||
colorType = "rgb";
|
|
||||||
}
|
|
||||||
color = color.replaceAll(",", "").replace(/.+?\(/, "").replace(")", "").replaceAll(/[ \t]+\/[ \t]+/g, " ").replaceAll("%", "").replaceAll("/", "");
|
|
||||||
if (colorType === "hsl") {
|
|
||||||
color = hslToHex(Number(color.split(" ")[0]), Number(color.split(" ")[1]), Number(color.split(" ")[2]));
|
|
||||||
}
|
|
||||||
if (colorType === "rgb") {
|
|
||||||
color = rgbToHex(Number(color.split(" ")[0]), Number(color.split(" ")[1]), Number(color.split(" ")[2]));
|
|
||||||
}
|
|
||||||
return color.replace("#", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
export const parseClr = (clr: number) => (clr & 0x00ffffff).toString(16).padStart(6, "0");
|
|
||||||
|
|
||||||
export async function getRepainterTheme(link: string): Promise<{ status: "success" | "fail", id?: string, colors?: string[], errorCode?: number, errorMsg?: string; }> {
|
|
||||||
const linkCheck: string | undefined = link.match(/https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&//=]*)/g)!.filter(x => x.startsWith("https://repainter.app/themes/"))[0];
|
|
||||||
|
|
||||||
if (!linkCheck) return { status: "fail", errorCode: 0, errorMsg: "Invalid URL" };
|
|
||||||
|
|
||||||
// const res = await (
|
|
||||||
// await fetch(
|
|
||||||
// `https://repainter.app/_next/data/Z0BCpVYZyrdkss0k0zqLC/themes/${link.match(/themes\/([a-z0-9]+)/i)?.[1] ?? ""
|
|
||||||
// }.json`,
|
|
||||||
// {
|
|
||||||
// "headers": {
|
|
||||||
// "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
||||||
// "accept-language": "en-US,en;q=0.9",
|
|
||||||
// "if-none-match": "W/\"4b2-Wsw1gFTK1l04ijqMn5s6ZUnH6hM\"",
|
|
||||||
// "priority": "u=0, i",
|
|
||||||
// "sec-ch-ua": "\"Chromium\";v=\"125\", \"Not.A/Brand\";v=\"24\"",
|
|
||||||
// "sec-ch-ua-mobile": "?0",
|
|
||||||
// "sec-ch-ua-platform": "\"Linux\"",
|
|
||||||
// "sec-fetch-dest": "document",
|
|
||||||
// "sec-fetch-mode": "navigate",
|
|
||||||
// "sec-fetch-site": "none",
|
|
||||||
// "sec-fetch-user": "?1",
|
|
||||||
// "upgrade-insecure-requests": "1"
|
|
||||||
// },
|
|
||||||
// "referrerPolicy": "strict-origin-when-cross-origin",
|
|
||||||
// "body": null,
|
|
||||||
// "method": "GET",
|
|
||||||
// "mode": "cors",
|
|
||||||
// "credentials": "omit",
|
|
||||||
// "cache": "no-store"
|
|
||||||
// },
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
const { pageProps: { fallback: { a: { name, colors } } } } = { "pageProps": { "initialId": "01G5PMR5G9H76H1R2RET4A0ZHY", "fallback": { a: { "id": "01G5PMR5G9H76H1R2RET4A0ZHY", "name": "Midwinter Fire", "description": "Very red", "createdAt": "2022-06-16T16:15:11.881Z", "updatedAt": "2022-07-12T08:37:13.141Z", "settingsLines": ["Colorful", "Bright", "Vibrant style"], "voteCount": 309, "colors": [-1426063361, 4294901760, 4294901760, -1426071591, -1426080078, -1426089335, 4294901760, -1426119398, -1428615936, -1431629312, -1434644480, 4294901760, 4294901760, 4294901760, 4294901760, -1426067223, -1426071086, -1426079070, -1426088082, 4294901760, -1428201216, -1430761216, -1433255936, 4294901760, 4294901760, 4294901760, 4294901760, 4294901760, 4294901760, -1426070330, 4294901760, -1426086346, 4294901760, -1430030080, 4294901760, -1434431744, 4294901760, 4294901760, 4294901760, 4294901760, -1426064133, 4294901760, -1426071591, 4294901760, -1426874223, 4294901760, -1430359452, 4294901760, -1433845194, 4294901760, -1437922816, 4294901760, 4294901760, 4294901760, 4294901760, -1426071591, -1426080078, -1426089335, -1427799438, -1429640356, 4294901760, -1433191891, 4294901760, 4294901760, 4294901760] } } }, "__N_SSP": true } as any;
|
|
||||||
return { status: "success", id: name, colors: colors.filter(c => c !== 4294901760).map(c => "#" + parseClr(c)) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prompts the user to choose a file from their system
|
|
||||||
* @param mimeTypes A comma separated list of mime types to accept, see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept#unique_file_type_specifiers
|
|
||||||
* @returns A promise that resolves to the chosen file or null if the user cancels
|
|
||||||
*/
|
|
||||||
export function chooseFile(mimeTypes: string) {
|
|
||||||
return new Promise<File | null>(resolve => {
|
|
||||||
const input = document.createElement("input");
|
|
||||||
input.type = "file";
|
|
||||||
input.style.display = "none";
|
|
||||||
input.accept = mimeTypes;
|
|
||||||
input.onchange = async () => {
|
|
||||||
resolve(input.files?.[0] ?? null);
|
|
||||||
};
|
|
||||||
|
|
||||||
document.body.appendChild(input);
|
|
||||||
input.click();
|
|
||||||
setImmediate(() => document.body.removeChild(input));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prompts the user to save a file to their system
|
|
||||||
* @param file The file to save
|
|
||||||
*/
|
|
||||||
export function saveFile(file: File) {
|
|
||||||
const a = document.createElement("a");
|
|
||||||
a.href = URL.createObjectURL(file);
|
|
||||||
a.download = file.name;
|
|
||||||
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
setImmediate(() => {
|
|
||||||
URL.revokeObjectURL(a.href);
|
|
||||||
document.body.removeChild(a);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function classes(...classes: Array<string | null | undefined | false>) {
|
|
||||||
return classes.filter(Boolean).join(" ");
|
|
||||||
}
|
|
|
@ -1,229 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DataStore } from ".";
|
|
||||||
import { ColorwayCSS } from "./colorwaysAPI";
|
|
||||||
import { updateBoundKeyMain, updateWSMain } from "./components/MainModal";
|
|
||||||
import { updateActiveColorway, updateManagerRole, updateWS as updateWSSelector } from "./components/Selector";
|
|
||||||
import { nullColorwayObj } from "./constants";
|
|
||||||
import { generateCss } from "./css";
|
|
||||||
import { ColorwayObject } from "./types";
|
|
||||||
import { colorToHex } from "./utils";
|
|
||||||
|
|
||||||
export let wsOpen = false;
|
|
||||||
export let boundKey: { [managerKey: string]: string; } | null = null;
|
|
||||||
export let hasManagerRole: boolean = false;
|
|
||||||
|
|
||||||
export let sendColorway: (obj: ColorwayObject) => void = () => { };
|
|
||||||
export let requestManagerRole: () => void = () => { };
|
|
||||||
export let updateRemoteSources: () => void = () => { };
|
|
||||||
export let closeWS: () => void = () => { };
|
|
||||||
export let restartWS: () => void = () => connect();
|
|
||||||
export let updateShouldAutoconnect: (shouldAutoconnect: boolean) => void = () => connect();
|
|
||||||
|
|
||||||
function updateWS(status: boolean) {
|
|
||||||
updateWSSelector(status);
|
|
||||||
updateWSMain(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateBoundKey(bound: { [managerKey: string]: string; }) {
|
|
||||||
updateBoundKeyMain(bound);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function connect() {
|
|
||||||
var ws: WebSocket | null = new WebSocket("ws://localhost:6124");
|
|
||||||
|
|
||||||
updateShouldAutoconnect = shouldAutoconnect => {
|
|
||||||
if (shouldAutoconnect && ws?.readyState === ws?.CLOSED) connect();
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onopen = function () {
|
|
||||||
wsOpen = true;
|
|
||||||
hasManagerRole = false;
|
|
||||||
updateWS(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
restartWS = () => {
|
|
||||||
ws?.close();
|
|
||||||
connect();
|
|
||||||
};
|
|
||||||
closeWS = () => ws?.close();
|
|
||||||
|
|
||||||
ws.onmessage = function (e) {
|
|
||||||
const data: {
|
|
||||||
type: "change-colorway" | "remove-colorway" | "manager-connection-established" | "complication:remote-sources:received" | "complication:remote-sources:update-request" | "complication:manager-role:granted" | "complication:manager-role:revoked",
|
|
||||||
[key: string]: any;
|
|
||||||
} = JSON.parse(e.data);
|
|
||||||
|
|
||||||
function typeSwitch(type) {
|
|
||||||
switch (type) {
|
|
||||||
case "change-colorway":
|
|
||||||
if (data.active.id == null) {
|
|
||||||
DataStore.set("activeColorwayObject", nullColorwayObj);
|
|
||||||
ColorwayCSS.remove();
|
|
||||||
updateActiveColorway(nullColorwayObj);
|
|
||||||
} else {
|
|
||||||
const demandedColorway = generateCss(
|
|
||||||
colorToHex("#" + data.active.colors.primary || "#313338").replace("#", ""),
|
|
||||||
colorToHex("#" + data.active.colors.secondary || "#2b2d31").replace("#", ""),
|
|
||||||
colorToHex("#" + data.active.colors.tertiary || "#1e1f22").replace("#", ""),
|
|
||||||
colorToHex("#" + data.active.colors.accent || "#5865f2").replace("#", "")
|
|
||||||
);
|
|
||||||
ColorwayCSS.set(demandedColorway);
|
|
||||||
DataStore.set("activeColorwayObject", { ...data.active, css: demandedColorway });
|
|
||||||
updateActiveColorway({ ...data.active, css: demandedColorway });
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case "remove-colorway":
|
|
||||||
DataStore.set("activeColorwayObject", nullColorwayObj);
|
|
||||||
ColorwayCSS.remove();
|
|
||||||
updateActiveColorway(nullColorwayObj);
|
|
||||||
return;
|
|
||||||
case "manager-connection-established":
|
|
||||||
DataStore.get("colorwaysBoundManagers").then((boundManagers: { [managerKey: string]: string; }[]) => {
|
|
||||||
if (data.MID) {
|
|
||||||
const boundSearch = boundManagers.filter(boundManager => {
|
|
||||||
if (Object.keys(boundManager)[0] === data.MID) return boundManager;
|
|
||||||
});
|
|
||||||
if (boundSearch.length) {
|
|
||||||
boundKey = boundSearch[0];
|
|
||||||
} else {
|
|
||||||
const id = { [data.MID]: `vencord.${Math.random().toString(16).slice(2)}.${new Date().getUTCMilliseconds()}` };
|
|
||||||
DataStore.set("colorwaysBoundManagers", [...boundManagers, id]);
|
|
||||||
boundKey = id;
|
|
||||||
}
|
|
||||||
updateBoundKey(typeof boundKey === "string" ? JSON.parse(boundKey) : boundKey);
|
|
||||||
ws?.send(JSON.stringify({
|
|
||||||
type: "client-sync-established",
|
|
||||||
boundKey,
|
|
||||||
complications: [
|
|
||||||
"remote-sources",
|
|
||||||
"manager-role"
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
DataStore.getMany([
|
|
||||||
"colorwaySourceFiles",
|
|
||||||
"customColorways"
|
|
||||||
]).then(([
|
|
||||||
colorwaySourceFiles,
|
|
||||||
customColorways
|
|
||||||
]) => {
|
|
||||||
ws?.send(JSON.stringify({
|
|
||||||
type: "complication:remote-sources:init",
|
|
||||||
boundKey,
|
|
||||||
online: colorwaySourceFiles,
|
|
||||||
offline: customColorways
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
sendColorway = obj => ws?.send(JSON.stringify({
|
|
||||||
type: "complication:manager-role:send-colorway",
|
|
||||||
active: obj,
|
|
||||||
boundKey
|
|
||||||
}));
|
|
||||||
requestManagerRole = () => ws?.send(JSON.stringify({
|
|
||||||
type: "complication:manager-role:request",
|
|
||||||
boundKey
|
|
||||||
}));
|
|
||||||
updateRemoteSources = () => DataStore.getMany([
|
|
||||||
"colorwaySourceFiles",
|
|
||||||
"customColorways"
|
|
||||||
]).then(([
|
|
||||||
colorwaySourceFiles,
|
|
||||||
customColorways
|
|
||||||
]) => {
|
|
||||||
ws?.send(JSON.stringify({
|
|
||||||
type: "complication:remote-sources:init",
|
|
||||||
boundKey,
|
|
||||||
online: colorwaySourceFiles,
|
|
||||||
offline: customColorways
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
case "complication:manager-role:granted":
|
|
||||||
hasManagerRole = true;
|
|
||||||
updateManagerRole(true);
|
|
||||||
return;
|
|
||||||
case "complication:manager-role:revoked":
|
|
||||||
hasManagerRole = false;
|
|
||||||
updateManagerRole(false);
|
|
||||||
return;
|
|
||||||
case "complication:remote-sources:update-request":
|
|
||||||
DataStore.getMany([
|
|
||||||
"colorwaySourceFiles",
|
|
||||||
"customColorways"
|
|
||||||
]).then(([
|
|
||||||
colorwaySourceFiles,
|
|
||||||
customColorways
|
|
||||||
]) => {
|
|
||||||
ws?.send(JSON.stringify({
|
|
||||||
type: "complication:remote-sources:init",
|
|
||||||
boundKey,
|
|
||||||
online: colorwaySourceFiles,
|
|
||||||
offline: customColorways
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typeSwitch(data.type);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onclose = function (e) {
|
|
||||||
boundKey = null;
|
|
||||||
hasManagerRole = false;
|
|
||||||
sendColorway = () => { };
|
|
||||||
requestManagerRole = () => { };
|
|
||||||
updateRemoteSources = () => { };
|
|
||||||
restartWS = () => connect();
|
|
||||||
closeWS = () => { };
|
|
||||||
try {
|
|
||||||
ws?.close();
|
|
||||||
} catch (e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ws = null;
|
|
||||||
wsOpen = false;
|
|
||||||
updateWS(false);
|
|
||||||
DataStore.getMany([
|
|
||||||
"colorwaysManagerAutoconnectPeriod",
|
|
||||||
"colorwaysManagerDoAutoconnect"
|
|
||||||
]).then(([
|
|
||||||
colorwaysManagerAutoconnectPeriod,
|
|
||||||
colorwaysManagerDoAutoconnect
|
|
||||||
]) => {
|
|
||||||
// eslint-disable-next-line no-constant-condition
|
|
||||||
if (colorwaysManagerDoAutoconnect || true) setTimeout(() => connect(), colorwaysManagerAutoconnectPeriod || 3000);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onerror = function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
boundKey = null;
|
|
||||||
sendColorway = () => { };
|
|
||||||
requestManagerRole = () => { };
|
|
||||||
updateRemoteSources = () => { };
|
|
||||||
restartWS = () => connect();
|
|
||||||
closeWS = () => { };
|
|
||||||
hasManagerRole = false;
|
|
||||||
ws?.close();
|
|
||||||
ws = null;
|
|
||||||
wsOpen = false;
|
|
||||||
updateWS(false);
|
|
||||||
DataStore.getMany([
|
|
||||||
"colorwaysManagerAutoconnectPeriod",
|
|
||||||
"colorwaysManagerDoAutoconnect"
|
|
||||||
]).then(([
|
|
||||||
colorwaysManagerAutoconnectPeriod,
|
|
||||||
colorwaysManagerDoAutoconnect
|
|
||||||
]) => {
|
|
||||||
// eslint-disable-next-line no-constant-condition
|
|
||||||
if (colorwaysManagerDoAutoconnect || true) setTimeout(() => connect(), colorwaysManagerAutoconnectPeriod || 3000);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -151,7 +151,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: 'backgroundColor:"COMPLETE"',
|
find: 'backgroundColor:"COMPLETE"',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=hasProfileEffect.+?)children:\[/,
|
match: /(?<=backgroundImage.+?)children:\[/,
|
||||||
replace: "$&$self.renderProfileTimezone(arguments[0]),"
|
replace: "$&$self.renderProfileTimezone(arguments[0]),"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue