/** * useThreads — multi-conversation sidebar with IndexedDB persistence. * Each thread has its own message list (separate IDB key). */ import { useCallback, useEffect, useState } from 'react' import { get, set, del } from 'idb-keyval' const THREADS_META_KEY = 'careless-threads-meta' export interface ThreadMeta { id: string title: string createdAt: number updatedAt: number messageCount: number pinned?: boolean archived?: boolean } export function threadKey(id: string) { return `careless-thread-${id}` } export function useThreads() { const [threads, setThreads] = useState([]) const [activeId, setActiveId] = useState(null) const load = useCallback(async () => { const meta = (await get(THREADS_META_KEY)) || [] setThreads(meta) if (!activeId && meta.length) setActiveId(meta[0].id) }, [activeId]) useEffect(() => { load() }, []) const save = async (next: ThreadMeta[]) => { await set(THREADS_META_KEY, next) setThreads(next) } const create = useCallback(async (title = 'New thread') => { const id = 't-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 6) const meta: ThreadMeta = { id, title, createdAt: Date.now(), updatedAt: Date.now(), messageCount: 0, } await save([meta, ...threads]) setActiveId(id) return id }, [threads]) const remove = useCallback(async (id: string) => { await del(threadKey(id)) const next = threads.filter(t => t.id !== id) await save(next) if (activeId === id) setActiveId(next[0]?.id || null) }, [threads, activeId]) const rename = useCallback(async (id: string, title: string) => { await save(threads.map(t => t.id === id ? { ...t, title, updatedAt: Date.now() } : t)) }, [threads]) const togglePin = useCallback(async (id: string) => { await save(threads.map(t => t.id === id ? { ...t, pinned: !t.pinned } : t)) }, [threads]) const updateMeta = useCallback(async (id: string, patch: Partial) => { await save(threads.map(t => t.id === id ? { ...t, ...patch, updatedAt: Date.now() } : t)) }, [threads]) return { threads, activeId, setActiveId, create, remove, rename, togglePin, updateMeta, reload: load, storageKey: activeId ? threadKey(activeId) : null, } }