Files
EPEEAIKit/art-agent/frontend/src/lib/store.ts
2026-04-15 00:21:43 +08:00

171 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 基于 localStorage 的客户端持久化存储。
* 提供会话、标签、图片资源的 CRUD 操作。
* 所有 key 按 user_id 隔离,确保多用户数据不混淆。
*/
import type { Session, Tag, ImageAsset, ChatMessage } from "./types";
// 当前登录用户的 ID由 AppProvider 在初始化时设置
let _userId = "";
export function setStoreUserId(id: string) {
_userId = id;
}
export function getStoreUserId(): string {
return _userId;
}
function storageKey(base: string): string {
const prefix = _userId || "_anonymous";
return `epeekit-${prefix}-${base}`;
}
// --------------- 内置标签 ---------------
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<T>(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<T>(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<Tag[]>(storageKey("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(storageKey("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<Session[]>(storageKey("sessions"), []);
}
export function saveSessions(sessions: Session[]) {
writeJSON(storageKey("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<ImageAsset[]>(storageKey("assets"), []);
}
export function saveAssets(assets: ImageAsset[]) {
writeJSON(storageKey("assets"), assets);
}
export function addAsset(asset: ImageAsset) {
const assets = loadAssets();
assets.unshift(asset);
saveAssets(assets);
}
export function updateAsset(assetId: string, patch: Partial<ImageAsset>) {
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;
}