import { create } from 'zustand' import { storage, SETTING_DEFAULTS } from '@/lib/storage' // 重新导出供 UI 层使用 export { SETTING_DEFAULTS } // --------------------------------------------------------------------------- // 类型定义 // --------------------------------------------------------------------------- /** 默认值表的 key 类型 */ type SettingKey = keyof typeof SETTING_DEFAULTS /** 从默认值表的 key 推导出 value 类型 */ type SettingValue = (typeof SETTING_DEFAULTS)[K] /** 每个 resettable key 在 store 中生成的四个字段名 */ type ResettableFields = { [P in K]: V } & { [P in `${K}Default`]: V } & { [P in `set${Capitalize}`]: (v: V) => void } & { [P in `reset${Capitalize}`]: () => void } /** 合并所有 resettable key 的字段为完整 SettingsStore 类型 */ type SettingsStore = ResettableFields<'playbackTempoSyncEnabled', boolean> & ResettableFields<'playbackHardSeekSyncEnabled', boolean> & ResettableFields<'syncPacketIntervalSeconds', number> & ResettableFields<'ttmlEnabled', boolean> & ResettableFields<'ttmlDbUrl', string> & ResettableFields<'lyricAlignAnchor', 'top' | 'center' | 'bottom'> & ResettableFields<'lyricAlignPosition', number> & ResettableFields<'lyricEnableSpring', boolean> & ResettableFields<'lyricEnableBlur', boolean> & ResettableFields<'lyricEnableScale', boolean> & ResettableFields<'lyricFontWeight', number> & ResettableFields<'lyricFontSize', number> & ResettableFields<'lyricTranslationFontSize', number> & ResettableFields<'lyricRomanFontSize', number> & { lyricOffsets: Record setLyricOffset: (key: string, offsetMs: number) => void clearLyricOffset: (key: string) => void } & ResettableFields<'bgFps', number> & ResettableFields<'bgFlowSpeed', number> & ResettableFields<'bgRenderScale', number> // --------------------------------------------------------------------------- // Store 实现 // --------------------------------------------------------------------------- /** 首字母大写 */ function capitalize(s: string): string { return s.charAt(0).toUpperCase() + s.slice(1) } export const useSettingsStore = create((set) => { /** * resettable 工厂:为每个 setting key 生成 4 个 store 字段 * key → 当前值 * keyDefault → 默认值(只读) * setKey → setter(持久化 + 更新 store) * resetKey → 恢复默认值 */ function resettable( key: K, load: () => SettingValue, persist: (v: SettingValue) => void, ): Record { const defaultValue = SETTING_DEFAULTS[key] const initial = load() const cap = capitalize(key) const setValue = (v: SettingValue) => { persist(v) set({ [key]: v } as Partial) } return { [key]: initial, [`${key}Default`]: defaultValue, [`set${cap}`]: setValue, [`reset${cap}`]: () => setValue(defaultValue), } } return { ...resettable('playbackTempoSyncEnabled', storage.getPlaybackTempoSyncEnabled, storage.setPlaybackTempoSyncEnabled), ...resettable( 'playbackHardSeekSyncEnabled', storage.getPlaybackHardSeekSyncEnabled, storage.setPlaybackHardSeekSyncEnabled, ), ...resettable( 'syncPacketIntervalSeconds', storage.getSyncPacketIntervalSeconds, storage.setSyncPacketIntervalSeconds, ), ...resettable('ttmlEnabled', storage.getTtmlEnabled, storage.setTtmlEnabled), ...resettable('ttmlDbUrl', storage.getTtmlDbUrl, storage.setTtmlDbUrl), ...resettable('lyricAlignAnchor', storage.getLyricAlignAnchor, storage.setLyricAlignAnchor), ...resettable('lyricAlignPosition', storage.getLyricAlignPosition, storage.setLyricAlignPosition), ...resettable('lyricEnableSpring', storage.getLyricEnableSpring, storage.setLyricEnableSpring), ...resettable('lyricEnableBlur', storage.getLyricEnableBlur, storage.setLyricEnableBlur), ...resettable('lyricEnableScale', storage.getLyricEnableScale, storage.setLyricEnableScale), ...resettable('lyricFontWeight', storage.getLyricFontWeight, storage.setLyricFontWeight), ...resettable('lyricFontSize', storage.getLyricFontSize, storage.setLyricFontSize), ...resettable('lyricTranslationFontSize', storage.getLyricTranslationFontSize, storage.setLyricTranslationFontSize), ...resettable('lyricRomanFontSize', storage.getLyricRomanFontSize, storage.setLyricRomanFontSize), lyricOffsets: storage.getLyricOffsets(), setLyricOffset: (key, offsetMs) => { const value = Math.round(Math.max(-10_000, Math.min(10_000, offsetMs))) set((state) => { const lyricOffsets = { ...state.lyricOffsets } if (value === 0) delete lyricOffsets[key] else lyricOffsets[key] = value storage.setLyricOffsets(lyricOffsets) return { lyricOffsets } }) }, clearLyricOffset: (key) => { set((state) => { if (!(key in state.lyricOffsets)) return state const lyricOffsets = { ...state.lyricOffsets } delete lyricOffsets[key] storage.setLyricOffsets(lyricOffsets) return { lyricOffsets } }) }, ...resettable('bgFps', storage.getBgFps, storage.setBgFps), ...resettable('bgFlowSpeed', storage.getBgFlowSpeed, storage.setBgFlowSpeed), ...resettable('bgRenderScale', storage.getBgRenderScale, storage.setBgRenderScale), } as SettingsStore })