mirror of
https://github.com/aykhans/slash-e.git
synced 2025-04-20 22:07:15 +00:00
feat: impl dark mode for extension
This commit is contained in:
parent
6126701025
commit
8c753e9557
@ -22,7 +22,7 @@ const ShortcutView = (props: Props) => {
|
|||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"group w-full px-3 py-2 flex flex-col justify-start items-start border rounded-lg hover:bg-gray-100 hover:shadow"
|
"group w-full px-3 py-2 flex flex-col justify-start items-start border rounded-lg hover:bg-gray-100 hover:shadow dark:border-zinc-800 dark:hover:bg-zinc-800"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="w-full flex flex-row justify-start items-center">
|
<div className="w-full flex flex-row justify-start items-center">
|
||||||
@ -42,13 +42,13 @@ const ShortcutView = (props: Props) => {
|
|||||||
onClick={handleShortcutLinkClick}
|
onClick={handleShortcutLinkClick}
|
||||||
>
|
>
|
||||||
<div className="truncate">
|
<div className="truncate">
|
||||||
<span>{shortcut.title}</span>
|
<span className="dark:text-gray-400">{shortcut.title}</span>
|
||||||
{shortcut.title ? (
|
{shortcut.title ? (
|
||||||
<span className="text-gray-400">(s/{shortcut.name})</span>
|
<span className="text-gray-500">(s/{shortcut.name})</span>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<span className="text-gray-400">s/</span>
|
<span className="text-gray-400 dark:text-gray-500">s/</span>
|
||||||
<span className="truncate">{shortcut.name}</span>
|
<span className="truncate dark:text-gray-400">{shortcut.name}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
43
frontend/extension/src/hooks/useColorTheme.ts
Normal file
43
frontend/extension/src/hooks/useColorTheme.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { useColorScheme } from "@mui/joy";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
const useColorTheme = () => {
|
||||||
|
const { mode: colorTheme, setMode: setColorTheme } = useColorScheme();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const root = document.documentElement;
|
||||||
|
if (colorTheme === "light") {
|
||||||
|
root.classList.remove("dark");
|
||||||
|
} else if (colorTheme === "dark") {
|
||||||
|
root.classList.add("dark");
|
||||||
|
} else {
|
||||||
|
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
if (darkMediaQuery.matches) {
|
||||||
|
root.classList.add("dark");
|
||||||
|
} else {
|
||||||
|
root.classList.remove("dark");
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleColorSchemeChange = (e: MediaQueryListEvent) => {
|
||||||
|
if (e.matches) {
|
||||||
|
root.classList.add("dark");
|
||||||
|
} else {
|
||||||
|
root.classList.remove("dark");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
darkMediaQuery.addEventListener("change", handleColorSchemeChange);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("failed to initial color scheme listener", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
darkMediaQuery.removeEventListener("change", handleColorSchemeChange);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [colorTheme]);
|
||||||
|
|
||||||
|
return { colorTheme, setColorTheme };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useColorTheme;
|
@ -1,5 +1,5 @@
|
|||||||
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
||||||
import { Button, Divider, Input } from "@mui/joy";
|
import { Button, CssVarsProvider, Divider, Input, Select, Option } from "@mui/joy";
|
||||||
import { useStorage } from "@plasmohq/storage/hook";
|
import { useStorage } from "@plasmohq/storage/hook";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Toaster, toast } from "react-hot-toast";
|
import { Toaster, toast } from "react-hot-toast";
|
||||||
@ -7,6 +7,7 @@ import Icon from "./components/Icon";
|
|||||||
import Logo from "./components/Logo";
|
import Logo from "./components/Logo";
|
||||||
import PullShortcutsButton from "./components/PullShortcutsButton";
|
import PullShortcutsButton from "./components/PullShortcutsButton";
|
||||||
import ShortcutsContainer from "./components/ShortcutsContainer";
|
import ShortcutsContainer from "./components/ShortcutsContainer";
|
||||||
|
import useColorTheme from "./hooks/useColorTheme";
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
|
||||||
interface SettingState {
|
interface SettingState {
|
||||||
@ -14,7 +15,23 @@ interface SettingState {
|
|||||||
accessToken: string;
|
accessToken: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const colorThemeOptions = [
|
||||||
|
{
|
||||||
|
value: "system",
|
||||||
|
label: "System",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "light",
|
||||||
|
label: "Light",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "dark",
|
||||||
|
label: "Dark",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const IndexOptions = () => {
|
const IndexOptions = () => {
|
||||||
|
const { colorTheme, setColorTheme } = useColorTheme();
|
||||||
const [domain, setDomain] = useStorage<string>("domain", (v) => (v ? v : ""));
|
const [domain, setDomain] = useStorage<string>("domain", (v) => (v ? v : ""));
|
||||||
const [accessToken, setAccessToken] = useStorage<string>("access_token", (v) => (v ? v : ""));
|
const [accessToken, setAccessToken] = useStorage<string>("access_token", (v) => (v ? v : ""));
|
||||||
const [settingState, setSettingState] = useState<SettingState>({
|
const [settingState, setSettingState] = useState<SettingState>({
|
||||||
@ -44,12 +61,15 @@ const IndexOptions = () => {
|
|||||||
toast.success("Setting saved");
|
toast.success("Setting saved");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSelectColorTheme = async (colorTheme: string) => {
|
||||||
|
setColorTheme(colorTheme as any);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="w-full flex flex-row justify-center items-center">
|
<div className="w-full flex flex-row justify-center items-center">
|
||||||
<a
|
<a
|
||||||
className="bg-yellow-100 mt-12 py-2 px-3 rounded-full border flex flex-row justify-start items-center cursor-pointer shadow hover:underline hover:text-blue-600"
|
className="bg-yellow-100 dark:bg-yellow-500 dark:opacity-70 mt-12 py-2 px-3 rounded-full border dark:border-yellow-600 flex flex-row justify-start items-center cursor-pointer shadow hover:underline hover:text-blue-600"
|
||||||
href="https://github.com/boojack/slash#browser-extension"
|
href="https://github.com/boojack/slash#browser-extension"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
@ -60,7 +80,7 @@ const IndexOptions = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full max-w-lg mx-auto flex flex-col justify-start items-start mt-12">
|
<div className="w-full max-w-lg mx-auto flex flex-col justify-start items-start mt-12">
|
||||||
<h2 className="flex flex-row justify-start items-center mb-6 text-2xl">
|
<h2 className="flex flex-row justify-start items-center mb-6 text-2xl dark:text-gray-400">
|
||||||
<Logo className="w-10 h-auto mr-2" />
|
<Logo className="w-10 h-auto mr-2" />
|
||||||
<span>Slash</span>
|
<span>Slash</span>
|
||||||
<span className="mx-2 text-gray-400">/</span>
|
<span className="mx-2 text-gray-400">/</span>
|
||||||
@ -70,10 +90,10 @@ const IndexOptions = () => {
|
|||||||
<div className="w-full flex flex-col justify-start items-start">
|
<div className="w-full flex flex-col justify-start items-start">
|
||||||
<div className="w-full flex flex-col justify-start items-start mb-4">
|
<div className="w-full flex flex-col justify-start items-start mb-4">
|
||||||
<div className="mb-2 text-base w-full flex flex-row justify-between items-center">
|
<div className="mb-2 text-base w-full flex flex-row justify-between items-center">
|
||||||
<span>Domain</span>
|
<span className="dark:text-gray-400">Domain</span>
|
||||||
{domain !== "" && (
|
{domain !== "" && (
|
||||||
<a
|
<a
|
||||||
className="text-sm flex flex-row justify-start items-center hover:underline hover:text-blue-600"
|
className="text-sm flex flex-row justify-start items-center dark:text-gray-400 hover:underline hover:text-blue-600"
|
||||||
href={domain}
|
href={domain}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
@ -94,7 +114,7 @@ const IndexOptions = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full flex flex-col justify-start items-start">
|
<div className="w-full flex flex-col justify-start items-start">
|
||||||
<span className="mb-2 text-base">Access Token</span>
|
<span className="mb-2 text-base dark:text-gray-400">Access Token</span>
|
||||||
<div className="relative w-full">
|
<div className="relative w-full">
|
||||||
<Input
|
<Input
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@ -106,9 +126,28 @@ const IndexOptions = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full mt-6">
|
<div className="w-full mt-6 flex flex-row justify-end">
|
||||||
<Button onClick={handleSaveSetting}>Save</Button>
|
<Button onClick={handleSaveSetting}>Save</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Divider className="!my-6" />
|
||||||
|
|
||||||
|
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Preference</p>
|
||||||
|
|
||||||
|
<div className="w-full flex flex-row justify-between items-center">
|
||||||
|
<div className="flex flex-row justify-start items-center gap-x-1">
|
||||||
|
<span className="dark:text-gray-400">Color Theme</span>
|
||||||
|
</div>
|
||||||
|
<Select defaultValue={colorTheme} onChange={(_, value) => handleSelectColorTheme(value)}>
|
||||||
|
{colorThemeOptions.map((option) => {
|
||||||
|
return (
|
||||||
|
<Option key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</Option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isInitialized && (
|
{isInitialized && (
|
||||||
@ -116,7 +155,7 @@ const IndexOptions = () => {
|
|||||||
<Divider className="!my-6" />
|
<Divider className="!my-6" />
|
||||||
|
|
||||||
<h2 className="flex flex-row justify-start items-center mb-4">
|
<h2 className="flex flex-row justify-start items-center mb-4">
|
||||||
<span className="text-lg">Shortcuts</span>
|
<span className="text-lg dark:text-gray-400">Shortcuts</span>
|
||||||
<span className="text-gray-500 mr-1">({shortcuts.length})</span>
|
<span className="text-gray-500 mr-1">({shortcuts.length})</span>
|
||||||
<PullShortcutsButton />
|
<PullShortcutsButton />
|
||||||
</h2>
|
</h2>
|
||||||
@ -125,10 +164,16 @@ const IndexOptions = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Toaster position="top-center" />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default IndexOptions;
|
const Options = () => {
|
||||||
|
return (
|
||||||
|
<CssVarsProvider>
|
||||||
|
<IndexOptions />
|
||||||
|
<Toaster position="top-right" />
|
||||||
|
</CssVarsProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Options;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
|
||||||
import { Button, Divider, IconButton } from "@mui/joy";
|
import { Button, CssVarsProvider, Divider, IconButton } from "@mui/joy";
|
||||||
import { useStorage } from "@plasmohq/storage/hook";
|
import { useStorage } from "@plasmohq/storage/hook";
|
||||||
import { Toaster } from "react-hot-toast";
|
import { Toaster } from "react-hot-toast";
|
||||||
import CreateShortcutsButton from "@/components/CreateShortcutsButton";
|
import CreateShortcutsButton from "@/components/CreateShortcutsButton";
|
||||||
@ -7,9 +7,11 @@ import Icon from "@/components/Icon";
|
|||||||
import Logo from "@/components/Logo";
|
import Logo from "@/components/Logo";
|
||||||
import PullShortcutsButton from "@/components/PullShortcutsButton";
|
import PullShortcutsButton from "@/components/PullShortcutsButton";
|
||||||
import ShortcutsContainer from "@/components/ShortcutsContainer";
|
import ShortcutsContainer from "@/components/ShortcutsContainer";
|
||||||
|
import useColorTheme from "./hooks/useColorTheme";
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
|
||||||
const IndexPopup = () => {
|
const IndexPopup = () => {
|
||||||
|
useColorTheme();
|
||||||
const [domain] = useStorage<string>("domain", "");
|
const [domain] = useStorage<string>("domain", "");
|
||||||
const [accessToken] = useStorage<string>("access_token", "");
|
const [accessToken] = useStorage<string>("access_token", "");
|
||||||
const [shortcuts] = useStorage<Shortcut[]>("shortcuts", []);
|
const [shortcuts] = useStorage<Shortcut[]>("shortcuts", []);
|
||||||
@ -25,10 +27,9 @@ const IndexPopup = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<div className="w-full min-w-[512px] px-4 pt-4">
|
<div className="w-full min-w-[512px] px-4 pt-4">
|
||||||
<div className="w-full flex flex-row justify-between items-center">
|
<div className="w-full flex flex-row justify-between items-center">
|
||||||
<div className="flex flex-row justify-start items-center">
|
<div className="flex flex-row justify-start items-center dark:text-gray-400">
|
||||||
<Logo className="w-6 h-auto mr-2" />
|
<Logo className="w-6 h-auto mr-2" />
|
||||||
<span className="">Slash</span>
|
<span className="">Slash</span>
|
||||||
{isInitialized && (
|
{isInitialized && (
|
||||||
@ -59,22 +60,15 @@ const IndexPopup = () => {
|
|||||||
<div className="w-full flex flex-row justify-between items-center mb-2">
|
<div className="w-full flex flex-row justify-between items-center mb-2">
|
||||||
<div className="flex flex-row justify-start items-center">
|
<div className="flex flex-row justify-start items-center">
|
||||||
<IconButton size="sm" variant="plain" color="neutral" onClick={handleSettingButtonClick}>
|
<IconButton size="sm" variant="plain" color="neutral" onClick={handleSettingButtonClick}>
|
||||||
<Icon.Settings className="w-5 h-auto text-gray-500" />
|
<Icon.Settings className="w-5 h-auto text-gray-500 dark:text-gray-400" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton size="sm" variant="plain" color="neutral" component="a" href="https://github.com/boojack/slash" target="_blank">
|
||||||
size="sm"
|
<Icon.Github className="w-5 h-auto text-gray-500 dark:text-gray-400" />
|
||||||
variant="plain"
|
|
||||||
color="neutral"
|
|
||||||
component="a"
|
|
||||||
href="https://github.com/boojack/slash"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<Icon.Github className="w-5 h-auto text-gray-500" />
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row justify-end items-center">
|
<div className="flex flex-row justify-end items-center">
|
||||||
<a
|
<a
|
||||||
className="text-sm flex flex-row justify-start items-center text-gray-500 hover:underline hover:text-blue-600"
|
className="text-sm flex flex-row justify-start items-center text-gray-500 dark:text-gray-400 hover:underline hover:text-blue-600"
|
||||||
href={domain}
|
href={domain}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
@ -87,12 +81,12 @@ const IndexPopup = () => {
|
|||||||
) : (
|
) : (
|
||||||
<div className="w-full flex flex-col justify-start items-center">
|
<div className="w-full flex flex-col justify-start items-center">
|
||||||
<Icon.Cookie strokeWidth={1} className="w-20 h-auto mb-4 text-gray-400" />
|
<Icon.Cookie strokeWidth={1} className="w-20 h-auto mb-4 text-gray-400" />
|
||||||
<p>Please set your domain and access token first.</p>
|
<p className="dark:text-gray-400">Please set your domain and access token first.</p>
|
||||||
<div className="w-full flex flex-row justify-center items-center py-4">
|
<div className="w-full flex flex-row justify-center items-center py-4">
|
||||||
<Button size="sm" color="primary" onClick={handleSettingButtonClick}>
|
<Button size="sm" color="primary" onClick={handleSettingButtonClick}>
|
||||||
<Icon.Settings className="w-5 h-auto mr-1" /> Setting
|
<Icon.Settings className="w-5 h-auto mr-1" /> Setting
|
||||||
</Button>
|
</Button>
|
||||||
<span className="mx-2">Or</span>
|
<span className="mx-2 dark:text-gray-400">Or</span>
|
||||||
<Button size="sm" variant="outlined" color="neutral" onClick={handleRefreshButtonClick}>
|
<Button size="sm" variant="outlined" color="neutral" onClick={handleRefreshButtonClick}>
|
||||||
<Icon.RefreshCcw className="w-5 h-auto mr-1" /> Refresh
|
<Icon.RefreshCcw className="w-5 h-auto mr-1" /> Refresh
|
||||||
</Button>
|
</Button>
|
||||||
@ -101,10 +95,16 @@ const IndexPopup = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Toaster position="top-right" />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default IndexPopup;
|
const Popup = () => {
|
||||||
|
return (
|
||||||
|
<CssVarsProvider>
|
||||||
|
<IndexPopup />
|
||||||
|
<Toaster position="top-right" />
|
||||||
|
</CssVarsProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Popup;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
body,
|
body,
|
||||||
html,
|
html,
|
||||||
#root {
|
#root {
|
||||||
@apply text-base;
|
@apply text-base dark:bg-zinc-900;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans", "Noto Sans CJK SC", "Microsoft YaHei UI", "Microsoft YaHei",
|
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans", "Noto Sans CJK SC", "Microsoft YaHei UI", "Microsoft YaHei",
|
||||||
"WenQuanYi Micro Hei", sans-serif, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
|
"WenQuanYi Micro Hei", sans-serif, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
|
||||||
"Noto Color Emoji";
|
"Noto Color Emoji";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user