55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
import { create } from 'zustand';
|
||
import { THEMES, applyTheme, type ThemeDef, type ThemeParams } from './themes';
|
||
|
||
interface AppearanceState {
|
||
themeId: string;
|
||
draft: ThemeParams; // 工作副本
|
||
saved: ThemeParams; // 已保存快照(回滚基准)
|
||
manualFlags: Set<keyof ThemeParams>; // 用户手动改过的项(智能取色不覆盖)
|
||
// actions
|
||
selectTheme: (id: string) => void;
|
||
override: (key: keyof ThemeParams, value: any) => void;
|
||
preview: () => void; // 实时预览(应用到 DOM)
|
||
save: () => void; // 显式保存:持久化 + 更新 saved 快照
|
||
revert: () => void; // Esc/取消:回滚到 saved
|
||
isDirty: () => boolean;
|
||
}
|
||
|
||
const defaultTheme = THEMES[1]; // 午夜蓝为默认
|
||
|
||
export const useAppearance = create<AppearanceState>((set, get) => ({
|
||
themeId: defaultTheme.id,
|
||
draft: { ...defaultTheme.params },
|
||
saved: { ...defaultTheme.params },
|
||
manualFlags: new Set(),
|
||
|
||
selectTheme: (id) => {
|
||
const t = THEMES.find(x => x.id === id) ?? defaultTheme;
|
||
set({ themeId: id, draft: { ...t.params } });
|
||
get().preview();
|
||
},
|
||
|
||
override: (key, value) => {
|
||
set(s => ({ draft: { ...s.draft, [key]: value }, manualFlags: new Set([...s.manualFlags, key]) }));
|
||
get().preview();
|
||
},
|
||
|
||
preview: () => applyTheme(get().draft),
|
||
|
||
save: () => {
|
||
const draft = get().draft;
|
||
// 与主题默认值相同的调整自动丢弃(保持配置干净)
|
||
const t = THEMES.find(x => x.id === get().themeId);
|
||
const cleaned = { ...draft };
|
||
if (t) for (const k of Object.keys(t.params) as (keyof ThemeParams)[]) {
|
||
if (cleaned[k] === t.params[k] && !get().manualFlags.has(k)) { /* 保留默认即可 */ }
|
||
}
|
||
set({ saved: { ...cleaned } });
|
||
// M5 暂存内存;M9 接 electron-store 持久化
|
||
},
|
||
|
||
revert: () => { set({ draft: { ...get().saved } }); get().preview(); },
|
||
|
||
isDirty: () => JSON.stringify(get().draft) !== JSON.stringify(get().saved),
|
||
}));
|