| import { create } from 'zustand' |
| import { storage, SETTING_DEFAULTS } from '@/lib/storage' |
|
|
| |
| export { SETTING_DEFAULTS } |
|
|
| |
| |
| |
|
|
| |
| type SettingKey = keyof typeof SETTING_DEFAULTS |
|
|
| |
| type SettingValue<K extends SettingKey> = (typeof SETTING_DEFAULTS)[K] |
|
|
| |
| type ResettableFields<K extends string, V> = { |
| [P in K]: V |
| } & { |
| [P in `${K}Default`]: V |
| } & { |
| [P in `set${Capitalize<K>}`]: (v: V) => void |
| } & { |
| [P in `reset${Capitalize<K>}`]: () => void |
| } |
|
|
| |
| 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<string, number> |
| setLyricOffset: (key: string, offsetMs: number) => void |
| clearLyricOffset: (key: string) => void |
| } & ResettableFields<'bgFps', number> & |
| ResettableFields<'bgFlowSpeed', number> & |
| ResettableFields<'bgRenderScale', number> |
|
|
| |
| |
| |
|
|
| |
| function capitalize(s: string): string { |
| return s.charAt(0).toUpperCase() + s.slice(1) |
| } |
|
|
| export const useSettingsStore = create<SettingsStore>((set) => { |
| |
| |
| |
| |
| |
| |
| |
| function resettable<K extends SettingKey>( |
| key: K, |
| load: () => SettingValue<K>, |
| persist: (v: SettingValue<K>) => void, |
| ): Record<string, unknown> { |
| const defaultValue = SETTING_DEFAULTS[key] |
| const initial = load() |
| const cap = capitalize(key) |
|
|
| const setValue = (v: SettingValue<K>) => { |
| persist(v) |
| set({ [key]: v } as Partial<SettingsStore>) |
| } |
|
|
| 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 |
| }) |
|
|