// ── Workspace persistence (localStorage) ────────────────────────────────────── // // Serialises and deserialises the full workspace state — all blocks in all // three tab panes, their positions and data, plus SVG connection topology. // // Depends on: dom.js ($, $id, svgNamespace, STORAGE_KEY) // helpers.js (refreshAll, splitValueAndUnit) // draw.js (drawpath) // /** * Reads the position and type-specific data from a single block element. * Delegates to the block's saveData() method for type-specific fields. * @param {HTMLElement} block - a WCBlock or WCProgram child element * @returns {{ x: number, y: number, ... }} */ function saveBlockData(block) { const data = { x: parseInt(block.style.left) || 0, y: parseInt(block.style.top) || 0 }; Object.assign(data, block.saveData()); return data; } /** * Serialises the entire workspace to localStorage under STORAGE_KEY. * Captures all blocks from algorithm, measurements, and data panes, * plus every SVG connection path. */ function saveWorkspace() { const prog = $id("main-program"); const dataArea = $id("data-area"); const varArea = $id("variable-area"); const connectionsSvg = $id("connections"); if (!prog || !dataArea || !varArea || !connectionsSvg) { showError("Cannot save: missing core UI elements"); return; } const state = { version: 1, counter: prog.counter, blocks: { algorithm: [], measurements: [], data: [], procedures: [] }, connections: [], sections: typeof saveSections === "function" ? saveSections() : [] }; for (const block of prog.children) { if (!block.saveData) continue; state.blocks.algorithm.push({ id: block.id, tag: block.tagName.toLowerCase(), data: saveBlockData(block) }); } for (const block of dataArea.children) { if (!block.saveData) continue; state.blocks.measurements.push({ id: block.id, tag: block.tagName.toLowerCase(), data: saveBlockData(block) }); } for (const block of varArea.children) { if (!block.saveData) continue; state.blocks.data.push({ id: block.id, tag: block.tagName.toLowerCase(), data: saveBlockData(block) }); } for (const block of document.querySelectorAll(".lib-pane .procedures-area > [data-block]")) { state.blocks.procedures.push({ id: block.id, tag: block.tagName.toLowerCase(), data: saveBlockData(block) }); } for (const path of connectionsSvg.querySelectorAll("path.flow, path.flow-data")) { state.connections.push({ fromBlock: path.fromElement ? path.fromElement.id : "", fromHook: path.fromHook ? (path.fromHook.id || null) : null, toBlock: path.toElement ? path.toElement.id : "", toHook: path.toHook ? (path.toHook.id || null) : null }); } localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } /** * Deep-clones a block