fix: [Mnemosyne] Memory persistence export — dump spatial state to JSON
Closes #1188 Automated squash merge by mimo swarm.
This commit is contained in:
@@ -652,11 +652,93 @@ const SpatialMemory = (() => {
|
||||
return _selectedId;
|
||||
}
|
||||
|
||||
// ─── FILE EXPORT ──────────────────────────────────────
|
||||
function exportToFile() {
|
||||
const index = exportIndex();
|
||||
const json = JSON.stringify(index, null, 2);
|
||||
const date = new Date().toISOString().slice(0, 10);
|
||||
const filename = 'mnemosyne-export-' + date + '.json';
|
||||
|
||||
const blob = new Blob([json], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
console.info('[Mnemosyne] Exported', index.memories.length, 'memories to', filename);
|
||||
return { filename, count: index.memories.length };
|
||||
}
|
||||
|
||||
// ─── FILE IMPORT ──────────────────────────────────────
|
||||
function importFromFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!file) {
|
||||
reject(new Error('No file provided'));
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
try {
|
||||
const data = JSON.parse(e.target.result);
|
||||
|
||||
// Schema validation
|
||||
if (!data || typeof data !== 'object') {
|
||||
reject(new Error('Invalid JSON: not an object'));
|
||||
return;
|
||||
}
|
||||
if (typeof data.version !== 'number') {
|
||||
reject(new Error('Invalid schema: missing version field'));
|
||||
return;
|
||||
}
|
||||
if (data.version !== STORAGE_VERSION) {
|
||||
reject(new Error('Version mismatch: got ' + data.version + ', expected ' + STORAGE_VERSION));
|
||||
return;
|
||||
}
|
||||
if (!Array.isArray(data.memories)) {
|
||||
reject(new Error('Invalid schema: memories is not an array'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate each memory entry
|
||||
for (let i = 0; i < data.memories.length; i++) {
|
||||
const mem = data.memories[i];
|
||||
if (!mem.id || typeof mem.id !== 'string') {
|
||||
reject(new Error('Invalid memory at index ' + i + ': missing or invalid id'));
|
||||
return;
|
||||
}
|
||||
if (!mem.category || typeof mem.category !== 'string') {
|
||||
reject(new Error('Invalid memory "' + mem.id + '": missing category'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const count = importIndex(data);
|
||||
saveToStorage();
|
||||
console.info('[Mnemosyne] Imported', count, 'memories from file');
|
||||
resolve({ count, total: data.memories.length });
|
||||
} catch (parseErr) {
|
||||
reject(new Error('Failed to parse JSON: ' + parseErr.message));
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = function() {
|
||||
reject(new Error('Failed to read file'));
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init, placeMemory, removeMemory, update,
|
||||
getMemoryAtPosition, getRegionAtPosition, getMemoriesInRegion, getAllMemories,
|
||||
getCrystalMeshes, getMemoryFromMesh, highlightMemory, clearHighlight, getSelectedId,
|
||||
exportIndex, importIndex, searchNearby, REGIONS,
|
||||
exportIndex, importIndex, exportToFile, importFromFile, searchNearby, REGIONS,
|
||||
saveToStorage, loadFromStorage, clearStorage,
|
||||
runGravityLayout
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user