import * as schema from '~~/drizzle/schema'; type UserSettings = typeof schema.settings.$inferSelect; type Appearance = UserSettings['appearance']; type SystemAssistants = UserSettings['systemAssistants']; const defaultAppearance: Appearance = { colorScheme: 'system', accent: 'violet', neutral: 'zinc', hinting: 0, fontSize: 'md', }; export const useUserSettings = async () => { const settings = useState('settings_state', () => null); const loaded = useState('settings_loaded', () => false); const syncing = useState('settings_syncing', () => false); const localAppearance = useState('settings_local_appearance', () => null); const localSystemAssistants = useState('settings_local_sa', () => null); const { refresh } = await useFetch('/api/settings', { key: 'settings_request', immediate: !loaded.value, onRequest() { loaded.value = true; }, onResponse({ response }) { if (response.ok && response._data) { settings.value = response._data; localAppearance.value = { ...defaultAppearance, ...response._data.appearance }; localSystemAssistants.value = response._data.systemAssistants; } } }); const applyOptimisticUpdates = (updates: { appearance?: Partial; systemAssistants?: Partial }) => { if (updates.appearance && localAppearance.value) { localAppearance.value = { ...localAppearance.value, ...updates.appearance }; } if (updates.systemAssistants && localSystemAssistants.value) { localSystemAssistants.value = { ...localSystemAssistants.value, ...updates.systemAssistants }; } if (updates.appearance && settings.value) { settings.value = { ...settings.value, appearance: { ...settings.value.appearance, ...updates.appearance } }; } if (updates.systemAssistants && settings.value) { settings.value = { ...settings.value, systemAssistants: { ...settings.value.systemAssistants, ...updates.systemAssistants } }; } }; const updateSettings = async (updates: { appearance?: Partial; systemAssistants?: Partial }) => { const original = settings.value; const originalAppearance = localAppearance.value; const originalSA = localSystemAssistants.value; applyOptimisticUpdates(updates); try { syncing.value = true; await $fetch(`/api/settings`, { method: 'PATCH', body: updates, }); await refresh(); } catch (error) { settings.value = original; localAppearance.value = originalAppearance; localSystemAssistants.value = originalSA; throw error; } finally { syncing.value = false; } }; const effectiveAppearance = computed(() => { return localAppearance.value || defaultAppearance; }); const effectiveSystemAssistants = computed(() => { return localSystemAssistants.value || {} as SystemAssistants; }); const colorSchemeValue = computed(() => { if (effectiveAppearance.value.colorScheme === 'system') { if (import.meta.client) { return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } return 'dark'; } return effectiveAppearance.value.colorScheme as 'dark' | 'light'; }); const effectiveSettings = computed(() => ({ appearance: effectiveAppearance.value, systemAssistants: effectiveSystemAssistants.value, })); return { settings: effectiveSettings, rawSettings: readonly(settings), updateSettings, syncing: readonly(syncing), loaded: readonly(loaded), refresh, accent: computed(() => effectiveAppearance.value.accent ?? 'violet'), neutral: computed(() => effectiveAppearance.value.neutral ?? 'zinc'), hinting: computed(() => effectiveAppearance.value.hinting ?? 0), colorScheme: { preference: computed(() => effectiveAppearance.value.colorScheme ?? 'system'), value: colorSchemeValue, class: computed(() => { if (effectiveAppearance.value.colorScheme === 'system') { if (import.meta.client) { return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } return 'dark'; } return effectiveAppearance.value.colorScheme as 'dark' | 'light'; }), } }; };