diff --git a/src/components/layout/use-custom-theme.ts b/src/components/layout/use-custom-theme.ts index 7ddd7d3..2aa07a6 100644 --- a/src/components/layout/use-custom-theme.ts +++ b/src/components/layout/use-custom-theme.ts @@ -1,7 +1,10 @@ import useSWR from "swr"; -import { useMemo } from "react"; +import { useEffect, useMemo } from "react"; +import { useRecoilState } from "recoil"; import { createTheme } from "@mui/material"; +import { appWindow } from "@tauri-apps/api/window"; import { getVergeConfig } from "../../services/cmds"; +import { atomThemeMode } from "../../services/states"; import { defaultTheme, defaultDarkTheme } from "../../pages/_theme"; /** @@ -10,10 +13,23 @@ import { defaultTheme, defaultDarkTheme } from "../../pages/_theme"; export default function useCustomTheme() { const { data } = useSWR("getVergeConfig", getVergeConfig); const { theme_mode, theme_setting } = data ?? {}; + const [mode, setMode] = useRecoilState(atomThemeMode); + + useEffect(() => { + if (theme_mode !== "system") { + setMode(theme_mode ?? "light"); + return; + } + + appWindow.theme().then((m) => m && setMode(m)); + const unlisten = appWindow.onThemeChanged((e) => setMode(e.payload)); + + return () => { + unlisten.then((fn) => fn()); + }; + }, [theme_mode]); const theme = useMemo(() => { - const mode = theme_mode ?? "light"; - const setting = theme_setting || {}; const dt = mode === "light" ? defaultTheme : defaultDarkTheme; @@ -78,7 +94,7 @@ export default function useCustomTheme() { }, 0); return theme; - }, [theme_mode, theme_setting]); + }, [mode, theme_setting]); return { theme }; } diff --git a/src/components/profile/file-editor.tsx b/src/components/profile/file-editor.tsx index 2b36b62..f7125ba 100644 --- a/src/components/profile/file-editor.tsx +++ b/src/components/profile/file-editor.tsx @@ -1,6 +1,6 @@ -import useSWR from "swr"; import { useEffect, useRef } from "react"; import { useLockFn } from "ahooks"; +import { useRecoilValue } from "recoil"; import { useTranslation } from "react-i18next"; import { Button, @@ -9,11 +9,8 @@ import { DialogContent, DialogTitle, } from "@mui/material"; -import { - getVergeConfig, - readProfileFile, - saveProfileFile, -} from "../../services/cmds"; +import { atomThemeMode } from "../../services/states"; +import { readProfileFile, saveProfileFile } from "../../services/cmds"; import Notice from "../base/base-notice"; import "monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js"; @@ -35,8 +32,7 @@ const FileEditor = (props: Props) => { const { t } = useTranslation(); const editorRef = useRef(); const instanceRef = useRef(null); - const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig); - const { theme_mode } = vergeConfig ?? {}; + const themeMode = useRecoilValue(atomThemeMode); useEffect(() => { if (!open) return; @@ -50,7 +46,7 @@ const FileEditor = (props: Props) => { instanceRef.current = editor.create(editorRef.current, { value: data, language: mode, - theme: theme_mode === "light" ? "vs" : "vs-dark", + theme: themeMode === "light" ? "vs" : "vs-dark", minimap: { enabled: false }, }); }); diff --git a/src/components/setting/palette-switch.tsx b/src/components/setting/palette-switch.tsx index 49b37f7..9f9c6b4 100644 --- a/src/components/setting/palette-switch.tsx +++ b/src/components/setting/palette-switch.tsx @@ -1,5 +1,6 @@ import { styled, Switch } from "@mui/material"; +// todo: deprecated // From: https://mui.com/components/switches/ const PaletteSwitch = styled(Switch)(({ theme }) => ({ width: 62, diff --git a/src/components/setting/setting-verge.tsx b/src/components/setting/setting-verge.tsx index 2241b96..f5f12cd 100644 --- a/src/components/setting/setting-verge.tsx +++ b/src/components/setting/setting-verge.tsx @@ -19,7 +19,7 @@ import { ArrowForward } from "@mui/icons-material"; import { SettingList, SettingItem } from "./setting"; import { CmdType } from "../../services/types"; import { version } from "../../../package.json"; -import PaletteSwitch from "./palette-switch"; +import ThemeModeSwitch from "./theme-mode-switch"; import GuardState from "./guard-state"; import SettingTheme from "./setting-theme"; @@ -43,19 +43,31 @@ const SettingVerge = ({ onError }: Props) => { return ( + + + e.target.value} + onChange={(e) => onChangeData({ language: e })} + onGuard={(e) => patchVergeConfig({ language: e })} + > + + + + onChangeData({ theme_mode: e ? "dark" : "light" })} - onGuard={(e) => - patchVergeConfig({ theme_mode: e ? "dark" : "light" }) - } + onChange={(e) => onChangeData({ theme_mode: e })} + onGuard={(e) => patchVergeConfig({ theme_mode: e })} > - + @@ -87,22 +99,6 @@ const SettingVerge = ({ onError }: Props) => { - - - e.target.value} - onChange={(e) => onChangeData({ language: e })} - onGuard={(e) => patchVergeConfig({ language: e })} - > - - - - { - + v{version} diff --git a/src/components/setting/theme-mode-switch.tsx b/src/components/setting/theme-mode-switch.tsx new file mode 100644 index 0000000..994cdd1 --- /dev/null +++ b/src/components/setting/theme-mode-switch.tsx @@ -0,0 +1,34 @@ +import { useTranslation } from "react-i18next"; +import { Button, ButtonGroup } from "@mui/material"; +import { CmdType } from "../../services/types"; + +type ThemeValue = CmdType.VergeConfig["theme_mode"]; + +interface Props { + value?: ThemeValue; + onChange?: (value: ThemeValue) => void; +} + +const ThemeModeSwitch = (props: Props) => { + const { value, onChange } = props; + const { t } = useTranslation(); + + const modes = ["light", "dark", "system"] as const; + + return ( + + {modes.map((mode) => ( + + ))} + + ); +}; + +export default ThemeModeSwitch; diff --git a/src/locales/en.json b/src/locales/en.json index 87ac308..7b75b85 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -55,7 +55,10 @@ "Language": "Language", "Open App Dir": "Open App Dir", "Open Logs Dir": "Open Logs Dir", - "Version": "Version", + "Verge Version": "Verge Version", + "theme.light": "Light", + "theme.dark": "Dark", + "theme.system": "System", "Save": "Save", "Cancel": "Cancel" diff --git a/src/locales/zh.json b/src/locales/zh.json index bd6f7b2..ea560ae 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -48,14 +48,17 @@ "System Proxy": "系统代理", "Proxy Guard": "系统代理守卫", "Proxy Bypass": "Proxy Bypass", - "Theme Mode": "暗夜模式", + "Theme Mode": "主题模式", "Theme Blur": "背景模糊", "Theme Setting": "主题设置", "Traffic Graph": "流量图显", "Language": "语言设置", "Open App Dir": "应用目录", "Open Logs Dir": "日志目录", - "Version": "版本", + "Verge Version": "应用版本", + "theme.light": "浅色", + "theme.dark": "深色", + "theme.system": "系统", "Save": "保存", "Cancel": "取消" diff --git a/src/services/states.ts b/src/services/states.ts index 015f484..6d7ebea 100644 --- a/src/services/states.ts +++ b/src/services/states.ts @@ -1,6 +1,11 @@ import { atom } from "recoil"; import { ApiType } from "./types"; +export const atomThemeMode = atom<"light" | "dark">({ + key: "atomThemeMode", + default: "light", +}); + export const atomClashPort = atom({ key: "atomClashPort", default: 0, diff --git a/src/services/types.ts b/src/services/types.ts index 44b8e7b..2edea25 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -126,7 +126,7 @@ export namespace CmdType { export interface VergeConfig { language?: string; clash_core?: string; - theme_mode?: "light" | "dark"; + theme_mode?: "light" | "dark" | "system"; theme_blur?: boolean; traffic_graph?: boolean; enable_tun_mode?: boolean;