/** * 基于 localStorage 的客户端持久化存储。 * 提供会话、标签、图片资源的 CRUD 操作。 */ import type { Session, Tag, ImageAsset, ChatMessage } from "./types"; const STORAGE_KEYS = { sessions: "epeekit-sessions", tags: "epeekit-tags", assets: "epeekit-assets", } as const; // --------------- 内置标签 --------------- const BUILTIN_TAGS: Tag[] = [ { id: "tag-ui", name: "UI", color: "#6366f1", builtin: true }, { id: "tag-icon", name: "Icon", color: "#f59e0b", builtin: true }, { id: "tag-illustration", name: "原画", color: "#10b981", builtin: true }, { id: "tag-style", name: "风格探索", color: "#ec4899", builtin: true }, { id: "tag-character", name: "立绘", color: "#8b5cf6", builtin: true }, { id: "tag-concept", name: "概念图", color: "#06b6d4", builtin: true }, ]; // --------------- 通用 helpers --------------- function readJSON(key: string, fallback: T): T { if (typeof window === "undefined") return fallback; try { const raw = localStorage.getItem(key); return raw ? (JSON.parse(raw) as T) : fallback; } catch { return fallback; } } function writeJSON(key: string, data: T) { if (typeof window === "undefined") return; localStorage.setItem(key, JSON.stringify(data)); } // --------------- ID 生成 --------------- let counter = 0; export function generateId(prefix = ""): string { counter++; const ts = Date.now().toString(36); const rand = Math.random().toString(36).slice(2, 6); return `${prefix}${ts}-${rand}-${counter}`; } // --------------- 标签 --------------- export function loadTags(): Tag[] { const custom = readJSON(STORAGE_KEYS.tags, []); const builtinIds = new Set(BUILTIN_TAGS.map((t) => t.id)); const merged = [...BUILTIN_TAGS, ...custom.filter((t) => !builtinIds.has(t.id))]; return merged; } export function saveTags(tags: Tag[]) { const custom = tags.filter((t) => !t.builtin); writeJSON(STORAGE_KEYS.tags, custom); } export function addTag(name: string, color: string): Tag { const tag: Tag = { id: generateId("tag-"), name, color, builtin: false }; const existing = loadTags(); saveTags([...existing, tag]); return tag; } export function deleteTag(tagId: string) { const tags = loadTags().filter((t) => t.id !== tagId && !t.builtin); saveTags(tags); } // --------------- 会话 --------------- export function loadSessions(): Session[] { return readJSON(STORAGE_KEYS.sessions, []); } export function saveSessions(sessions: Session[]) { writeJSON(STORAGE_KEYS.sessions, sessions); } export function createSession(): Session { const now = Date.now(); const session: Session = { id: generateId("sess-"), title: "新对话", tags: [], messages: [], createdAt: now, updatedAt: now, }; const sessions = loadSessions(); saveSessions([session, ...sessions]); return session; } export function updateSession(session: Session) { const sessions = loadSessions(); const idx = sessions.findIndex((s) => s.id === session.id); if (idx >= 0) { sessions[idx] = { ...session, updatedAt: Date.now() }; } else { sessions.unshift({ ...session, updatedAt: Date.now() }); } saveSessions(sessions); } export function deleteSession(sessionId: string) { const sessions = loadSessions().filter((s) => s.id !== sessionId); saveSessions(sessions); // 同时删除关联的图片资源 const assets = loadAssets().filter((a) => a.sessionId !== sessionId); saveAssets(assets); } // --------------- 图片资源 --------------- export function loadAssets(): ImageAsset[] { return readJSON(STORAGE_KEYS.assets, []); } export function saveAssets(assets: ImageAsset[]) { writeJSON(STORAGE_KEYS.assets, assets); } export function addAsset(asset: ImageAsset) { const assets = loadAssets(); assets.unshift(asset); saveAssets(assets); } export function updateAsset(assetId: string, patch: Partial) { const assets = loadAssets(); const idx = assets.findIndex((a) => a.id === assetId); if (idx >= 0) { assets[idx] = { ...assets[idx], ...patch }; saveAssets(assets); } } export function deleteAsset(assetId: string) { saveAssets(loadAssets().filter((a) => a.id !== assetId)); } export function toggleFavorite(assetId: string): boolean { const assets = loadAssets(); const idx = assets.findIndex((a) => a.id === assetId); if (idx >= 0) { assets[idx].favorited = !assets[idx].favorited; saveAssets(assets); return assets[idx].favorited; } return false; }