Files
logic-gates/js/saveLoad.js
Jose Luis 2fd22cc79d feat: persist circuit and components to localStorage
Auto-saves every 3 seconds and on page unload. Restores the full
state (circuit, camera, custom components) on page load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 04:25:28 +01:00

195 lines
5.4 KiB
JavaScript

// Save/Load system — export and import circuits and progress
import { state } from './state.js';
import { progress } from './levels.js';
const STORAGE_KEY = 'logiclab_state';
/**
* Save complete application state to JSON
*/
export function saveState() {
return {
timestamp: new Date().toISOString(),
circuit: {
gates: state.gates,
connections: state.connections,
nextId: state.nextId
},
camera: {
camX: state.camX,
camY: state.camY,
zoom: state.zoom
},
components: state.customComponents || {},
progress: {
unlockedLevels: progress.unlockedLevels,
completedLevels: progress.completedLevels,
currentLevel: progress.currentLevel,
customComponents: progress.customComponents
}
};
}
/**
* Load application state from JSON
*/
export function loadState(data) {
if (!data || !data.circuit) {
return { success: false, error: 'Invalid save data' };
}
try {
// Load circuit
state.gates = JSON.parse(JSON.stringify(data.circuit.gates));
state.connections = JSON.parse(JSON.stringify(data.circuit.connections));
state.nextId = data.circuit.nextId;
// Load camera
if (data.camera) {
state.camX = data.camera.camX;
state.camY = data.camera.camY;
state.zoom = data.camera.zoom;
}
// Load components
if (data.components) {
state.customComponents = JSON.parse(JSON.stringify(data.components));
}
// Load progress
if (data.progress) {
progress.unlockedLevels = data.progress.unlockedLevels || ['buffer'];
progress.completedLevels = data.progress.completedLevels || [];
progress.currentLevel = data.progress.currentLevel || null;
progress.customComponents = data.progress.customComponents || {};
}
return { success: true };
} catch (e) {
return { success: false, error: e.message };
}
}
/**
* Export as JSON file (for download)
*/
export function exportAsFile(filename = 'logic-circuit.json') {
const data = saveState();
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
/**
* Import from file (returns Promise)
*/
export function importFromFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result);
const result = loadState(data);
resolve(result);
} catch (err) {
reject(new Error('Failed to parse JSON'));
}
};
reader.onerror = () => reject(new Error('Failed to read file'));
reader.readAsText(file);
});
}
/**
* Export circuit as a simple netlist (text format)
*/
export function exportAsNetlist() {
let netlist = '# Logic Circuit Netlist\n\n';
netlist += '## Gates\n';
state.gates.forEach(gate => {
netlist += `${gate.type} ${gate.id} at (${gate.x.toFixed(0)}, ${gate.y.toFixed(0)})\n`;
});
netlist += '\n## Connections\n';
state.connections.forEach(conn => {
netlist += `${conn.from}:${conn.fromPort} -> ${conn.to}:${conn.toPort}\n`;
});
return netlist;
}
/**
* Copy state to clipboard as JSON (useful for sharing)
*/
export function copyToClipboard() {
const data = saveState();
const json = JSON.stringify(data);
navigator.clipboard.writeText(json).then(() => {
alert('State copied to clipboard');
}).catch(() => {
alert('Failed to copy to clipboard');
});
}
/**
* Paste state from clipboard
*/
export async function pasteFromClipboard() {
try {
const text = await navigator.clipboard.readText();
const data = JSON.parse(text);
return loadState(data);
} catch (e) {
return { success: false, error: e.message };
}
}
/**
* Save state to localStorage
*/
export function saveToStorage() {
try {
const data = saveState();
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
} catch (e) {
console.warn('[storage] failed to save:', e.message);
}
}
/**
* Load state from localStorage (returns true if state was restored)
*/
export function loadFromStorage() {
try {
const json = localStorage.getItem(STORAGE_KEY);
if (!json) return false;
const data = JSON.parse(json);
const result = loadState(data);
if (result.success) {
console.log('[storage] restored state from localStorage');
}
return result.success;
} catch (e) {
console.warn('[storage] failed to load:', e.message);
return false;
}
}
/**
* Start auto-saving to localStorage on an interval
*/
let autoSaveInterval = null;
export function startAutoSave(intervalMs = 3000) {
if (autoSaveInterval) clearInterval(autoSaveInterval);
autoSaveInterval = setInterval(saveToStorage, intervalMs);
// Also save on page unload
window.addEventListener('beforeunload', saveToStorage);
console.log(`[storage] auto-save enabled (every ${intervalMs}ms)`);
}