mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
Merge pull request #129 from GrothKeiran/feat/add-switchable-pure-white-theme
feat(theme): add switchable pure-white theme
This commit is contained in:
@@ -117,6 +117,12 @@ const headerIcons = {
|
||||
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9z" />
|
||||
</svg>
|
||||
),
|
||||
whiteTheme: (
|
||||
<svg {...headerIconProps}>
|
||||
<circle cx="12" cy="12" r="7" />
|
||||
<circle cx="12" cy="12" r="3" fill="currentColor" stroke="none" />
|
||||
</svg>
|
||||
),
|
||||
autoTheme: (
|
||||
<svg {...headerIconProps}>
|
||||
<defs>
|
||||
@@ -571,7 +577,9 @@ export function MainLayout() {
|
||||
? headerIcons.autoTheme
|
||||
: theme === 'dark'
|
||||
? headerIcons.moon
|
||||
: headerIcons.sun}
|
||||
: theme === 'white'
|
||||
? headerIcons.whiteTheme
|
||||
: headerIcons.sun}
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" onClick={logout} title={t('header.logout')}>
|
||||
{headerIcons.logout}
|
||||
|
||||
@@ -1400,6 +1400,7 @@
|
||||
"theme": {
|
||||
"switch": "Theme",
|
||||
"light": "Light",
|
||||
"white": "Pure White",
|
||||
"dark": "Dark",
|
||||
"switch_to_light": "Switch to light mode",
|
||||
"switch_to_dark": "Switch to dark mode",
|
||||
|
||||
@@ -1405,6 +1405,7 @@
|
||||
"theme": {
|
||||
"switch": "Тема",
|
||||
"light": "Светлая",
|
||||
"white": "Чисто-белая",
|
||||
"dark": "Тёмная",
|
||||
"switch_to_light": "Переключиться на светлую тему",
|
||||
"switch_to_dark": "Переключиться на тёмную тему",
|
||||
|
||||
@@ -1400,6 +1400,7 @@
|
||||
"theme": {
|
||||
"switch": "主题",
|
||||
"light": "亮色",
|
||||
"white": "纯白",
|
||||
"dark": "暗色",
|
||||
"switch_to_light": "切换到亮色模式",
|
||||
"switch_to_dark": "切换到暗色模式",
|
||||
|
||||
@@ -25,12 +25,28 @@ const getSystemTheme = (): ResolvedTheme => {
|
||||
return 'light';
|
||||
};
|
||||
|
||||
const applyTheme = (resolved: ResolvedTheme) => {
|
||||
const resolveTheme = (theme: Theme): ResolvedTheme | 'white' => {
|
||||
if (theme === 'auto') {
|
||||
return getSystemTheme();
|
||||
}
|
||||
if (theme === 'white') {
|
||||
return 'white';
|
||||
}
|
||||
return theme;
|
||||
};
|
||||
|
||||
const applyTheme = (resolved: ResolvedTheme | 'white') => {
|
||||
if (resolved === 'dark') {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
} else {
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
return;
|
||||
}
|
||||
|
||||
if (resolved === 'white') {
|
||||
document.documentElement.setAttribute('data-theme', 'white');
|
||||
return;
|
||||
}
|
||||
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
};
|
||||
|
||||
export const useThemeStore = create<ThemeState>()(
|
||||
@@ -40,14 +56,17 @@ export const useThemeStore = create<ThemeState>()(
|
||||
resolvedTheme: 'light',
|
||||
|
||||
setTheme: (theme) => {
|
||||
const resolved: ResolvedTheme = theme === 'auto' ? getSystemTheme() : theme;
|
||||
const resolved = resolveTheme(theme);
|
||||
applyTheme(resolved);
|
||||
set({ theme, resolvedTheme: resolved });
|
||||
set({
|
||||
theme,
|
||||
resolvedTheme: resolved === 'white' ? 'light' : resolved,
|
||||
});
|
||||
},
|
||||
|
||||
cycleTheme: () => {
|
||||
const { theme, setTheme } = get();
|
||||
const order: Theme[] = ['light', 'dark', 'auto'];
|
||||
const order: Theme[] = ['light', 'white', 'dark', 'auto'];
|
||||
const currentIndex = order.indexOf(theme);
|
||||
const nextTheme = order[(currentIndex + 1) % order.length];
|
||||
setTheme(nextTheme);
|
||||
|
||||
@@ -56,6 +56,59 @@
|
||||
--accent-tertiary: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
// 纯白主题
|
||||
[data-theme='white'] {
|
||||
--bg-secondary: #ffffff;
|
||||
--bg-primary: #ffffff;
|
||||
--bg-tertiary: #f6f6f6;
|
||||
--bg-hover: var(--bg-tertiary);
|
||||
--bg-quinary: #ffffff;
|
||||
--bg-error-light: rgba(198, 87, 70, 0.08);
|
||||
|
||||
--text-primary: #2d2a26;
|
||||
--text-secondary: #6d6760;
|
||||
--text-tertiary: #a29c95;
|
||||
--text-quaternary: #c0bab3;
|
||||
--text-muted: var(--text-tertiary);
|
||||
|
||||
--border-color: #e5e5e5;
|
||||
--border-secondary: var(--border-color);
|
||||
--border-primary: #d9d9d9;
|
||||
--border-hover: #cccccc;
|
||||
|
||||
--primary-color: #8b8680;
|
||||
--primary-hover: #7f7a74;
|
||||
--primary-active: #726d67;
|
||||
--primary-contrast: #ffffff;
|
||||
|
||||
--success-color: #10b981;
|
||||
--warning-color: #c65746;
|
||||
--error-color: #c65746;
|
||||
--danger-color: var(--error-color);
|
||||
--info-color: var(--primary-color);
|
||||
|
||||
--warning-bg: rgba(198, 87, 70, 0.12);
|
||||
--warning-border: rgba(198, 87, 70, 0.35);
|
||||
--warning-text: var(--warning-color);
|
||||
|
||||
--success-badge-bg: #d1fae5;
|
||||
--success-badge-text: #065f46;
|
||||
--success-badge-border: #6ee7b7;
|
||||
|
||||
--failure-badge-bg: rgba(198, 87, 70, 0.14);
|
||||
--failure-badge-text: #8a3a30;
|
||||
--failure-badge-border: rgba(198, 87, 70, 0.35);
|
||||
|
||||
--count-badge-bg: rgba(139, 134, 128, 0.18);
|
||||
--count-badge-text: var(--primary-active);
|
||||
|
||||
--shadow: 0 1px 2px 0 rgb(0 0 0 / 0.08);
|
||||
--shadow-lg: 0 10px 18px -3px rgb(0 0 0 / 0.1);
|
||||
|
||||
--radius-md: 8px;
|
||||
--accent-tertiary: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
// 深色主题(#191919)
|
||||
[data-theme='dark'] {
|
||||
// 极简暖灰:深色模式(提升对比度与层级)
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
* 通用类型定义
|
||||
*/
|
||||
|
||||
export type Theme = 'light' | 'dark' | 'auto';
|
||||
export type Theme = 'light' | 'white' | 'dark' | 'auto';
|
||||
|
||||
export type Language = 'zh-CN' | 'en' | 'ru';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user