import type { UIMessage } from "@ai-sdk/react"; import { LazyStore } from "@tauri-apps/plugin-store"; export type SessionMeta = { id: string; title: string; createdAt: number; updatedAt: number; }; const STORE_PATH = "terax-ai-sessions.json"; const KEY_SESSIONS = "sessions"; const KEY_ACTIVE = "activeId"; const messagesKey = (id: string) => `messages:${id}`; const store = new LazyStore(STORE_PATH, { defaults: {}, autoSave: 200 }); export type LoadedSessions = { sessions: SessionMeta[]; activeId: string | null; }; export async function loadAll(): Promise { // One IPC roundtrip via entries() rather than two parallel get()s. Per- // session messages are loaded lazily via `loadMessages` only when a // session is opened, so cold boot stays at a single store call. const entries = await store.entries(); let sessions: SessionMeta[] | undefined; let activeId: string | null | undefined; for (const [k, v] of entries) { if (k === KEY_SESSIONS) sessions = v as SessionMeta[]; else if (k === KEY_ACTIVE) activeId = v as string | null; } return { sessions: sessions ?? [], activeId: activeId ?? null }; } export async function loadMessages(id: string): Promise { return (await store.get(messagesKey(id))) ?? null; } export async function saveSessionsList(sessions: SessionMeta[]): Promise { await store.set(KEY_SESSIONS, sessions); } export async function saveActiveId(id: string | null): Promise { await store.set(KEY_ACTIVE, id); } export async function saveMessages( id: string, messages: UIMessage[], ): Promise { await store.set(messagesKey(id), messages); } export async function deleteSessionData(id: string): Promise { await store.delete(messagesKey(id)); } export function newSessionId(): string { return `s-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; } export function deriveTitle(messages: UIMessage[]): string { for (const m of messages) { if (m.role !== "user") continue; for (const p of m.parts) { if (p.type !== "text") continue; const text = (p as { text: string }).text .replace(/\s*/g, "") .replace(/\s*/g, "") .replace(/\s*/g, "") .trim(); if (!text) continue; const first = text.split("\n")[0].trim(); return first.length > 40 ? `${first.slice(0, 40)}…` : first; } } return "New chat"; }