export const BOARD_SESSION_KEY = "matrix-board-session-v1"; function validContext(context) { return context === null || ( context && ["template", "animation"].includes(context.type) && ["id", "name", "revision", "baseline"].every((key) => typeof context[key] === "string" && context[key]) && typeof context.readOnly === "boolean" && (context.type !== "animation" || context.readOnly) ); } export function readBoardSession(storage, draft, bootId) { const raw = storage?.getItem(BOARD_SESSION_KEY); if (!raw) return { bootId: null, context: null }; try { const value = JSON.parse(raw); if (value.version !== 1 || typeof value.bootId !== "string" || !validContext(value.context)) { throw new Error("invalid session"); } return { bootId: value.bootId, context: value.bootId === bootId && value.draft === draft ? value.context : null, }; } catch { throw new Error("画板保存绑定损坏或来自未来版本;原值已保留,请另存当前内容后处理浏览器存储。"); } } export function writeBoardSession(storage, bootId, draft, context) { storage.setItem(BOARD_SESSION_KEY, JSON.stringify({ version: 1, bootId, draft, context })); } // Resolve only editable static defaults and the read-only demo animation. // Ordinary animations retain their existing explicit frame editing workflow. export async function loadDefaultBoard(apiJson) { const reference = await apiJson("/api/display/default-content"); if (reference.type === "template") { return { ...await apiJson(`/api/templates/${reference.id}`), type: "template" }; } if (reference.type !== "animation" || !reference.is_demo) return null; const animation = await apiJson(`/api/animations/${reference.id}/frames`); const first = animation.frames[0]; if (!first) throw new Error("默认演示动图没有帧"); const frame = await apiJson(`/api/animations/${reference.id}/frames/${first.id}`); return { ...animation, type: "animation", read_only: true, scene: frame.scene }; } export async function initializeDefaultBoard({ canLoad, load, apply, retain }) { if (!canLoad()) return false; const content = await load(); if (!canLoad()) { retain(); return false; } if (content) apply(content); else retain(); return Boolean(content); }