diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx index 4a0938ce4..3d7504f8a 100644 --- a/src/components/Icons.tsx +++ b/src/components/Icons.tsx @@ -308,3 +308,21 @@ export function NoEntrySignIcon(props: IconProps) { ); } + +export function PasteIcon(props: IconProps) { + return ( + + + + + ); +} diff --git a/src/components/ThemeSettings/UserCSSModal.tsx b/src/components/ThemeSettings/UserCSSModal.tsx index 6cf3c9b15..100005a90 100644 --- a/src/components/ThemeSettings/UserCSSModal.tsx +++ b/src/components/ThemeSettings/UserCSSModal.tsx @@ -4,11 +4,12 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { useSettings } from "@api/Settings"; +import { Settings, useSettings } from "@api/Settings"; import { Flex } from "@components/Flex"; +import { CopyIcon, PasteIcon } from "@components/Icons"; import { copyWithToast } from "@utils/misc"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; -import { Button, showToast, Text, Toasts } from "@webpack/common"; +import { showToast, Text, Toasts, Tooltip } from "@webpack/common"; import { type ReactNode } from "react"; import { UserstyleHeader } from "usercss-meta"; @@ -19,6 +20,59 @@ interface UserCSSSettingsModalProps { theme: UserstyleHeader; } +function ExportButton({ themeSettings }: { themeSettings: Settings["userCssVars"][""]; }) { + return + {({ onMouseLeave, onMouseEnter }) => ( +
{ + copyWithToast(JSON.stringify(themeSettings), "Copied theme settings to clipboard."); + }}> + +
+ )} +
; +} + +function ImportButton({ themeSettings }: { themeSettings: Settings["userCssVars"][""]; }) { + return + {({ onMouseLeave, onMouseEnter }) => ( +
{ + const clip = (await navigator.clipboard.read())[0]; + + if (!clip) return showToast("Your clipboard is empty.", Toasts.Type.FAILURE); + + if (!clip.types.includes("text/plain")) + return showToast("Your clipboard doesn't have valid settings data.", Toasts.Type.FAILURE); + + try { + var potentialSettings: Record = + JSON.parse(await clip.getType("text/plain").then(b => b.text())); + } catch (e) { + return showToast("Your clipboard doesn't have valid settings data.", Toasts.Type.FAILURE); + } + + for (const [key, value] of Object.entries(potentialSettings)) { + if (Object.prototype.hasOwnProperty.call(themeSettings, key)) + themeSettings[key] = value; + } + + showToast("Pasted theme settings from clipboard.", Toasts.Type.SUCCESS); + }}> + +
+ )} +
; +} + export function UserCSSSettingsModal({ modalProps, theme }: UserCSSSettingsModalProps) { // @ts-expect-error UseSettings<> can't determine this is a valid key const themeSettings = useSettings(["userCssVars"], false).userCssVars[theme.id]; @@ -105,37 +159,14 @@ export function UserCSSSettingsModal({ modalProps, theme }: UserCSSSettingsModal Settings for {theme.name} + + + + -
- - -
{controls}
diff --git a/src/components/ThemeSettings/themesStyles.css b/src/components/ThemeSettings/themesStyles.css index 62e95f001..1af279bb9 100644 --- a/src/components/ThemeSettings/themesStyles.css +++ b/src/components/ThemeSettings/themesStyles.css @@ -28,8 +28,13 @@ content: "by "; } -.vc-settings-usercss-ie-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-gap: 12px; /* matching the flex gap */ +.vc-settings-usercss-ie-buttons > div { + color: var(--interactive-normal); + opacity: .5; + padding: 4px; +} + +.vc-settings-usercss-ie-buttons > div:hover { + color: var(--interactive-hover); + opacity: 1; }