mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 05:03:45 +08:00
feat: theme mode support follows system
This commit is contained in:
parent
115e604627
commit
3d1b6d7de7
@ -1,7 +1,10 @@
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
|
import { useRecoilState } from "recoil";
|
||||||
import { createTheme } from "@mui/material";
|
import { createTheme } from "@mui/material";
|
||||||
|
import { appWindow } from "@tauri-apps/api/window";
|
||||||
import { getVergeConfig } from "../../services/cmds";
|
import { getVergeConfig } from "../../services/cmds";
|
||||||
|
import { atomThemeMode } from "../../services/states";
|
||||||
import { defaultTheme, defaultDarkTheme } from "../../pages/_theme";
|
import { defaultTheme, defaultDarkTheme } from "../../pages/_theme";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -10,10 +13,23 @@ import { defaultTheme, defaultDarkTheme } from "../../pages/_theme";
|
|||||||
export default function useCustomTheme() {
|
export default function useCustomTheme() {
|
||||||
const { data } = useSWR("getVergeConfig", getVergeConfig);
|
const { data } = useSWR("getVergeConfig", getVergeConfig);
|
||||||
const { theme_mode, theme_setting } = data ?? {};
|
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 theme = useMemo(() => {
|
||||||
const mode = theme_mode ?? "light";
|
|
||||||
|
|
||||||
const setting = theme_setting || {};
|
const setting = theme_setting || {};
|
||||||
const dt = mode === "light" ? defaultTheme : defaultDarkTheme;
|
const dt = mode === "light" ? defaultTheme : defaultDarkTheme;
|
||||||
|
|
||||||
@ -78,7 +94,7 @@ export default function useCustomTheme() {
|
|||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
return theme;
|
return theme;
|
||||||
}, [theme_mode, theme_setting]);
|
}, [mode, theme_setting]);
|
||||||
|
|
||||||
return { theme };
|
return { theme };
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import useSWR from "swr";
|
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
|
import { useRecoilValue } from "recoil";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -9,11 +9,8 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import {
|
import { atomThemeMode } from "../../services/states";
|
||||||
getVergeConfig,
|
import { readProfileFile, saveProfileFile } from "../../services/cmds";
|
||||||
readProfileFile,
|
|
||||||
saveProfileFile,
|
|
||||||
} from "../../services/cmds";
|
|
||||||
import Notice from "../base/base-notice";
|
import Notice from "../base/base-notice";
|
||||||
|
|
||||||
import "monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js";
|
import "monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js";
|
||||||
@ -35,8 +32,7 @@ const FileEditor = (props: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const editorRef = useRef<any>();
|
const editorRef = useRef<any>();
|
||||||
const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
||||||
const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig);
|
const themeMode = useRecoilValue(atomThemeMode);
|
||||||
const { theme_mode } = vergeConfig ?? {};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return;
|
if (!open) return;
|
||||||
@ -50,7 +46,7 @@ const FileEditor = (props: Props) => {
|
|||||||
instanceRef.current = editor.create(editorRef.current, {
|
instanceRef.current = editor.create(editorRef.current, {
|
||||||
value: data,
|
value: data,
|
||||||
language: mode,
|
language: mode,
|
||||||
theme: theme_mode === "light" ? "vs" : "vs-dark",
|
theme: themeMode === "light" ? "vs" : "vs-dark",
|
||||||
minimap: { enabled: false },
|
minimap: { enabled: false },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { styled, Switch } from "@mui/material";
|
import { styled, Switch } from "@mui/material";
|
||||||
|
|
||||||
|
// todo: deprecated
|
||||||
// From: https://mui.com/components/switches/
|
// From: https://mui.com/components/switches/
|
||||||
const PaletteSwitch = styled(Switch)(({ theme }) => ({
|
const PaletteSwitch = styled(Switch)(({ theme }) => ({
|
||||||
width: 62,
|
width: 62,
|
||||||
|
@ -19,7 +19,7 @@ import { ArrowForward } from "@mui/icons-material";
|
|||||||
import { SettingList, SettingItem } from "./setting";
|
import { SettingList, SettingItem } from "./setting";
|
||||||
import { CmdType } from "../../services/types";
|
import { CmdType } from "../../services/types";
|
||||||
import { version } from "../../../package.json";
|
import { version } from "../../../package.json";
|
||||||
import PaletteSwitch from "./palette-switch";
|
import ThemeModeSwitch from "./theme-mode-switch";
|
||||||
import GuardState from "./guard-state";
|
import GuardState from "./guard-state";
|
||||||
import SettingTheme from "./setting-theme";
|
import SettingTheme from "./setting-theme";
|
||||||
|
|
||||||
@ -43,19 +43,31 @@ const SettingVerge = ({ onError }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingList title={t("Verge Setting")}>
|
<SettingList title={t("Verge Setting")}>
|
||||||
|
<SettingItem>
|
||||||
|
<ListItemText primary={t("Language")} />
|
||||||
|
<GuardState
|
||||||
|
value={language ?? "en"}
|
||||||
|
onCatch={onError}
|
||||||
|
onFormat={(e: any) => e.target.value}
|
||||||
|
onChange={(e) => onChangeData({ language: e })}
|
||||||
|
onGuard={(e) => patchVergeConfig({ language: e })}
|
||||||
|
>
|
||||||
|
<Select size="small" sx={{ width: 100 }}>
|
||||||
|
<MenuItem value="zh">中文</MenuItem>
|
||||||
|
<MenuItem value="en">English</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</GuardState>
|
||||||
|
</SettingItem>
|
||||||
|
|
||||||
<SettingItem>
|
<SettingItem>
|
||||||
<ListItemText primary={t("Theme Mode")} />
|
<ListItemText primary={t("Theme Mode")} />
|
||||||
<GuardState
|
<GuardState
|
||||||
value={theme_mode === "dark"}
|
value={theme_mode}
|
||||||
valueProps="checked"
|
|
||||||
onCatch={onError}
|
onCatch={onError}
|
||||||
onFormat={onSwitchFormat}
|
onChange={(e) => onChangeData({ theme_mode: e })}
|
||||||
onChange={(e) => onChangeData({ theme_mode: e ? "dark" : "light" })}
|
onGuard={(e) => patchVergeConfig({ theme_mode: e })}
|
||||||
onGuard={(e) =>
|
|
||||||
patchVergeConfig({ theme_mode: e ? "dark" : "light" })
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<PaletteSwitch edge="end" />
|
<ThemeModeSwitch />
|
||||||
</GuardState>
|
</GuardState>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
@ -87,22 +99,6 @@ const SettingVerge = ({ onError }: Props) => {
|
|||||||
</GuardState>
|
</GuardState>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
<SettingItem>
|
|
||||||
<ListItemText primary={t("Language")} />
|
|
||||||
<GuardState
|
|
||||||
value={language ?? "en"}
|
|
||||||
onCatch={onError}
|
|
||||||
onFormat={(e: any) => e.target.value}
|
|
||||||
onChange={(e) => onChangeData({ language: e })}
|
|
||||||
onGuard={(e) => patchVergeConfig({ language: e })}
|
|
||||||
>
|
|
||||||
<Select size="small" sx={{ width: 100 }}>
|
|
||||||
<MenuItem value="zh">中文</MenuItem>
|
|
||||||
<MenuItem value="en">English</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</GuardState>
|
|
||||||
</SettingItem>
|
|
||||||
|
|
||||||
<SettingItem>
|
<SettingItem>
|
||||||
<ListItemText primary={t("Theme Setting")} />
|
<ListItemText primary={t("Theme Setting")} />
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -129,7 +125,7 @@ const SettingVerge = ({ onError }: Props) => {
|
|||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
<SettingItem>
|
<SettingItem>
|
||||||
<ListItemText primary={t("Version")} />
|
<ListItemText primary={t("Verge Version")} />
|
||||||
<Typography sx={{ py: "6px" }}>v{version}</Typography>
|
<Typography sx={{ py: "6px" }}>v{version}</Typography>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
|
34
src/components/setting/theme-mode-switch.tsx
Normal file
34
src/components/setting/theme-mode-switch.tsx
Normal file
@ -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 (
|
||||||
|
<ButtonGroup size="small">
|
||||||
|
{modes.map((mode) => (
|
||||||
|
<Button
|
||||||
|
key={mode}
|
||||||
|
variant={mode === value ? "contained" : "outlined"}
|
||||||
|
onClick={() => onChange?.(mode)}
|
||||||
|
sx={{ textTransform: "capitalize" }}
|
||||||
|
>
|
||||||
|
{t(`theme.${mode}`)}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</ButtonGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeModeSwitch;
|
@ -55,7 +55,10 @@
|
|||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
"Open App Dir": "Open App Dir",
|
"Open App Dir": "Open App Dir",
|
||||||
"Open Logs Dir": "Open Logs Dir",
|
"Open Logs Dir": "Open Logs Dir",
|
||||||
"Version": "Version",
|
"Verge Version": "Verge Version",
|
||||||
|
"theme.light": "Light",
|
||||||
|
"theme.dark": "Dark",
|
||||||
|
"theme.system": "System",
|
||||||
|
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"Cancel": "Cancel"
|
"Cancel": "Cancel"
|
||||||
|
@ -48,14 +48,17 @@
|
|||||||
"System Proxy": "系统代理",
|
"System Proxy": "系统代理",
|
||||||
"Proxy Guard": "系统代理守卫",
|
"Proxy Guard": "系统代理守卫",
|
||||||
"Proxy Bypass": "Proxy Bypass",
|
"Proxy Bypass": "Proxy Bypass",
|
||||||
"Theme Mode": "暗夜模式",
|
"Theme Mode": "主题模式",
|
||||||
"Theme Blur": "背景模糊",
|
"Theme Blur": "背景模糊",
|
||||||
"Theme Setting": "主题设置",
|
"Theme Setting": "主题设置",
|
||||||
"Traffic Graph": "流量图显",
|
"Traffic Graph": "流量图显",
|
||||||
"Language": "语言设置",
|
"Language": "语言设置",
|
||||||
"Open App Dir": "应用目录",
|
"Open App Dir": "应用目录",
|
||||||
"Open Logs Dir": "日志目录",
|
"Open Logs Dir": "日志目录",
|
||||||
"Version": "版本",
|
"Verge Version": "应用版本",
|
||||||
|
"theme.light": "浅色",
|
||||||
|
"theme.dark": "深色",
|
||||||
|
"theme.system": "系统",
|
||||||
|
|
||||||
"Save": "保存",
|
"Save": "保存",
|
||||||
"Cancel": "取消"
|
"Cancel": "取消"
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import { atom } from "recoil";
|
import { atom } from "recoil";
|
||||||
import { ApiType } from "./types";
|
import { ApiType } from "./types";
|
||||||
|
|
||||||
|
export const atomThemeMode = atom<"light" | "dark">({
|
||||||
|
key: "atomThemeMode",
|
||||||
|
default: "light",
|
||||||
|
});
|
||||||
|
|
||||||
export const atomClashPort = atom<number>({
|
export const atomClashPort = atom<number>({
|
||||||
key: "atomClashPort",
|
key: "atomClashPort",
|
||||||
default: 0,
|
default: 0,
|
||||||
|
@ -126,7 +126,7 @@ export namespace CmdType {
|
|||||||
export interface VergeConfig {
|
export interface VergeConfig {
|
||||||
language?: string;
|
language?: string;
|
||||||
clash_core?: string;
|
clash_core?: string;
|
||||||
theme_mode?: "light" | "dark";
|
theme_mode?: "light" | "dark" | "system";
|
||||||
theme_blur?: boolean;
|
theme_blur?: boolean;
|
||||||
traffic_graph?: boolean;
|
traffic_graph?: boolean;
|
||||||
enable_tun_mode?: boolean;
|
enable_tun_mode?: boolean;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user