import { writable } from "svelte/store"; import { get } from "svelte/store"; import { invoke } from "$lib/runtime/invoke"; const defaultTags = ["Personal", "Work", "Ideas", "Journal"]; const defaultFragmentTypes = ["Quote", "Snippet", "Reference"]; const startupViews = ["entries", "calendar", "fragments", "todos", "lists"] as const; const defaultStartupView = "entries"; export type StartupView = typeof startupViews[number]; export const settingsTags = writable([...defaultTags]); export const settingsFragmentTypes = writable([...defaultFragmentTypes]); export const settingsDefaultStartupView = writable(defaultStartupView); let hydrationComplete = false; let hydrating = false; type UiSettingsPayload = { tags?: string[]; fragmentTypes?: string[]; defaultStartupView?: string; }; function normalize(value: string): string { return value.trim().toLowerCase(); } function hasDuplicate(values: string[], candidate: string, excludeIndex?: number): boolean { const normalized = normalize(candidate); return values.some((value, index) => index !== excludeIndex && normalize(value) === normalized); } function normalizeValues(values: string[], fallback: string[]): string[] { const seen = new Set(); const result: string[] = []; for (const value of values) { const trimmed = value.trim(); if (!trimmed) continue; const key = normalize(trimmed); if (seen.has(key)) continue; seen.add(key); result.push(trimmed); } return result.length ? result : [...fallback]; } function normalizeStartupView(value: string | undefined): StartupView { const normalized = (value ?? "").trim().toLowerCase(); if (startupViews.includes(normalized as StartupView)) { return normalized as StartupView; } return defaultStartupView; } export async function hydrateUiSettings(): Promise { if (hydrating || hydrationComplete) return; hydrating = true; try { const payload = await invoke("get_ui_settings"); settingsTags.set(normalizeValues(payload.tags ?? [], defaultTags)); settingsFragmentTypes.set(normalizeValues(payload.fragmentTypes ?? [], defaultFragmentTypes)); settingsDefaultStartupView.set(normalizeStartupView(payload.defaultStartupView)); } catch (error) { console.error("[settings] hydrate failed", error); } finally { hydrating = false; hydrationComplete = true; } } export async function persistUiSettings(): Promise { const tags = normalizeValues(get(settingsTags), defaultTags); const fragmentTypes = normalizeValues(get(settingsFragmentTypes), defaultFragmentTypes); const startupView = normalizeStartupView(get(settingsDefaultStartupView)); settingsTags.set(tags); settingsFragmentTypes.set(fragmentTypes); settingsDefaultStartupView.set(startupView); await invoke("set_ui_settings", { tags, fragmentTypes, fragment_types: fragmentTypes, defaultStartupView: startupView, default_startup_view: startupView }); } function queuePersist(): void { if (!hydrationComplete || hydrating) return; void persistUiSettings().catch((error) => { console.error("[settings] persist failed", error); }); } export function addSettingsTag(value: string): boolean { const next = value.trim(); if (!next) return false; const tags = get(settingsTags); if (hasDuplicate(tags, next)) return false; settingsTags.set([...tags, next]); queuePersist(); return true; } export function updateSettingsTag(index: number, value: string): boolean { const next = value.trim(); if (!next) return false; const tags = get(settingsTags); if (index < 0 || index >= tags.length) return false; if (hasDuplicate(tags, next, index)) return false; settingsTags.set(tags.map((tag, idx) => (idx === index ? next : tag))); queuePersist(); return true; } export function removeSettingsTag(index: number): boolean { const tags = get(settingsTags); if (index < 0 || index >= tags.length) return false; settingsTags.set(tags.filter((_, idx) => idx !== index)); queuePersist(); return true; } export function addFragmentType(value: string): boolean { const next = value.trim(); if (!next) return false; const types = get(settingsFragmentTypes); if (hasDuplicate(types, next)) return false; settingsFragmentTypes.set([...types, next]); queuePersist(); return true; } export function updateFragmentType(index: number, value: string): boolean { const next = value.trim(); if (!next) return false; const types = get(settingsFragmentTypes); if (index < 0 || index >= types.length) return false; if (hasDuplicate(types, next, index)) return false; settingsFragmentTypes.set(types.map((type, idx) => (idx === index ? next : type))); queuePersist(); return true; } export function removeFragmentType(index: number): boolean { const types = get(settingsFragmentTypes); if (index < 0 || index >= types.length) return false; settingsFragmentTypes.set(types.filter((_, idx) => idx !== index)); queuePersist(); return true; } export function setDefaultStartupView(value: string): boolean { const next = normalizeStartupView(value); if (get(settingsDefaultStartupView) === next) return false; settingsDefaultStartupView.set(next); queuePersist(); return true; }