import { create } from 'zustand' import { settingsApi } from '../api/settings' import { modelsApi } from '../api/models' import type { PermissionMode, EffortLevel, ModelInfo, ThemeMode, WebSearchSettings } from '../types/settings' import type { Locale } from '../i18n' import { useUIStore } from './uiStore' const LOCALE_STORAGE_KEY = 'cc-haha-locale' let desktopNotificationsSaveQueue: Promise = Promise.resolve() function getStoredLocale(): Locale { try { const stored = localStorage.getItem(LOCALE_STORAGE_KEY) if (stored === 'en' || stored === 'zh') return stored } catch { /* localStorage unavailable */ } return 'zh' } type SettingsStore = { permissionMode: PermissionMode currentModel: ModelInfo | null effortLevel: EffortLevel thinkingEnabled: boolean availableModels: ModelInfo[] activeProviderName: string | null locale: Locale theme: ThemeMode skipWebFetchPreflight: boolean desktopNotificationsEnabled: boolean webSearch: WebSearchSettings isLoading: boolean error: string | null fetchAll: () => Promise setPermissionMode: (mode: PermissionMode) => Promise setModel: (modelId: string) => Promise setEffort: (level: EffortLevel) => Promise setThinkingEnabled: (enabled: boolean) => Promise setLocale: (locale: Locale) => void setTheme: (theme: ThemeMode) => Promise setSkipWebFetchPreflight: (enabled: boolean) => Promise setDesktopNotificationsEnabled: (enabled: boolean) => Promise setWebSearch: (settings: WebSearchSettings) => Promise } export const useSettingsStore = create((set, get) => ({ permissionMode: 'default', currentModel: null, effortLevel: 'medium', thinkingEnabled: true, availableModels: [], activeProviderName: null, locale: getStoredLocale(), theme: useUIStore.getState().theme, skipWebFetchPreflight: true, desktopNotificationsEnabled: true, webSearch: { mode: 'auto', tavilyApiKey: '', braveApiKey: '' }, isLoading: false, error: null, fetchAll: async () => { set({ isLoading: true, error: null }) try { const [{ mode }, modelsRes, { model }, { level }, userSettings] = await Promise.all([ settingsApi.getPermissionMode(), modelsApi.list(), modelsApi.getCurrent(), modelsApi.getEffort(), settingsApi.getUser(), ]) const theme = userSettings.theme === 'dark' ? 'dark' : 'light' useUIStore.getState().setTheme(theme) set({ permissionMode: mode, availableModels: modelsRes.models, activeProviderName: modelsRes.provider?.name ?? null, currentModel: model, effortLevel: level, thinkingEnabled: userSettings.alwaysThinkingEnabled !== false, theme, skipWebFetchPreflight: userSettings.skipWebFetchPreflight !== false, desktopNotificationsEnabled: userSettings.desktopNotificationsEnabled !== false, webSearch: normalizeWebSearchSettings(userSettings.webSearch), isLoading: false, error: null, }) } catch (error) { const message = error instanceof Error ? error.message : 'Failed to load desktop settings' set({ isLoading: false, error: message }) throw error } }, setPermissionMode: async (mode) => { const prev = get().permissionMode set({ permissionMode: mode }) try { await settingsApi.setPermissionMode(mode) } catch { set({ permissionMode: prev }) } }, setModel: async (modelId) => { await modelsApi.setCurrent(modelId) const { model } = await modelsApi.getCurrent() set({ currentModel: model }) }, setEffort: async (level) => { const prev = get().effortLevel set({ effortLevel: level }) try { await modelsApi.setEffort(level) } catch { set({ effortLevel: prev }) } }, setThinkingEnabled: async (enabled) => { const prev = get().thinkingEnabled set({ thinkingEnabled: enabled }) try { await settingsApi.updateUser({ alwaysThinkingEnabled: enabled ? undefined : false }) } catch { set({ thinkingEnabled: prev }) } }, setLocale: (locale) => { set({ locale }) try { localStorage.setItem(LOCALE_STORAGE_KEY, locale) } catch { /* noop */ } }, setTheme: async (theme) => { const prev = get().theme set({ theme }) useUIStore.getState().setTheme(theme) try { await settingsApi.updateUser({ theme }) } catch { set({ theme: prev }) useUIStore.getState().setTheme(prev) } }, setSkipWebFetchPreflight: async (enabled) => { const prev = get().skipWebFetchPreflight set({ skipWebFetchPreflight: enabled }) try { await settingsApi.updateUser({ skipWebFetchPreflight: enabled }) } catch { set({ skipWebFetchPreflight: prev }) } }, setDesktopNotificationsEnabled: async (enabled) => { const prev = get().desktopNotificationsEnabled set({ desktopNotificationsEnabled: enabled }) const save = desktopNotificationsSaveQueue .catch(() => undefined) .then(async () => { if (get().desktopNotificationsEnabled !== enabled) return await settingsApi.updateUser({ desktopNotificationsEnabled: enabled }) }) desktopNotificationsSaveQueue = save try { await save } catch { if (get().desktopNotificationsEnabled === enabled) { set({ desktopNotificationsEnabled: prev }) } } }, setWebSearch: async (webSearch) => { const prev = get().webSearch const next = normalizeWebSearchSettings(webSearch) set({ webSearch: next }) try { await settingsApi.updateUser({ webSearch: next }) } catch { set({ webSearch: prev }) } }, })) function normalizeWebSearchSettings(settings: WebSearchSettings | undefined): WebSearchSettings { return { mode: settings?.mode ?? 'auto', tavilyApiKey: settings?.tavilyApiKey ?? '', braveApiKey: settings?.braveApiKey ?? '', } }