/** * Conversation persistence for @geepers/chat * localStorage-backed with max 20 conversations, sorted by updatedAt. */ import type { ConversationRecord, Message, Provider } from '../types/index.js'; const DEFAULT_KEY = 'gchat_conversations'; const MAX_CONVOS = 20; export class ConversationStorage { private readonly key: string; constructor(storageKey = DEFAULT_KEY) { this.key = storageKey; } private read(): Record { try { return JSON.parse(localStorage.getItem(this.key) || '{}'); } catch { return {}; } } private write(data: Record): void { try { localStorage.setItem(this.key, JSON.stringify(data)); } catch { // Storage full or unavailable — skip silently } } all(): ConversationRecord[] { const data = this.read(); return Object.values(data).sort((a, b) => b.updatedAt - a.updatedAt); } get(id: string): ConversationRecord | undefined { return this.read()[id]; } save(record: ConversationRecord): void { const data = this.read(); data[record.id] = record; // Trim to max const sorted = Object.keys(data).sort( (a, b) => data[b].updatedAt - data[a].updatedAt ); if (sorted.length > MAX_CONVOS) { sorted.slice(MAX_CONVOS).forEach(k => delete data[k]); } this.write(data); } delete(id: string): void { const data = this.read(); delete data[id]; this.write(data); } /** Create a new ConversationRecord from the given messages */ static build( id: string, messages: Message[], provider: Provider, systemPrompt: string ): ConversationRecord { const first = messages.find(m => m.role === 'user'); const title = first ? first.content.slice(0, 50) + (first.content.length > 50 ? '…' : '') : 'New conversation'; return { id, title, messages, provider, systemPrompt, createdAt: Date.now(), updatedAt: Date.now(), }; } }