import type { Info, Message, Part, Session } from "./types"; /** * Read-only copy of the app's last state for the offline case (train, tunnel): the chat * list and the most recently opened chats. Kept small: attachments are dropped and long * tool output is cut. Everything is best-effort – storage may be full or blocked. */ const KEY = "codecraft.offline.v1"; const MAX_CHATS = 6; const MAX_MESSAGES = 80; const MAX_BYTES = 2_500_000; interface Cache { info?: Info; sessions?: Session[]; chats: Record; } function read(): Cache { try { const c = JSON.parse(localStorage.getItem(KEY) ?? "null"); if (c && typeof c === "object") return { chats: {}, ...c }; } catch { /* empty or corrupt */ } return { chats: {} }; } function write(c: Cache) { try { // Oldest chats go first when the copy gets too big. let text = JSON.stringify(c); while (text.length > MAX_BYTES && Object.keys(c.chats).length > 0) { const oldest = Object.entries(c.chats).sort((a, b) => a[1].at - b[1].at)[0][0]; delete c.chats[oldest]; text = JSON.stringify(c); } localStorage.setItem(KEY, text); } catch { /* storage full or unavailable */ } } function slim(part: Part): Part { if (part.type === "image") return { type: "text", text: "[image]" } as Part; if (part.type === "tool_result") return { ...part, content: part.content.slice(0, 1500), images: [], meta: undefined } as Part; return part; } export function cacheInfo(info: Info) { if (!info.user) return; write({ ...read(), info }); } export function cachedInfo(): Info | undefined { return read().info; } export function cacheSessions(sessions: Session[]) { write({ ...read(), sessions }); } export function cachedSessions(): Session[] { return read().sessions ?? []; } export function cacheChat(session: Session, messages: Message[]) { const c = read(); c.chats[session.id] = { at: Date.now(), session, messages: messages.slice(-MAX_MESSAGES).map((m) => ({ ...m, parts: m.parts.map(slim) })), }; const ids = Object.entries(c.chats).sort((a, b) => b[1].at - a[1].at); for (const [id] of ids.slice(MAX_CHATS)) delete c.chats[id]; write(c); } export function cachedChat(id: string) { return read().chats[id]; } /** A request failed because there is no connection (as opposed to an answer from the server). */ export function isOffline(e: unknown): boolean { return e instanceof TypeError || !navigator.onLine; }