feat(labs): Фаза2 — сохранение/возобновление параметров симуляции

Поверх getState/applyState: в обычном режиме параметры активной симуляции
персистятся в localStorage (lab-sim-state-v1, дедуп, кап 8КБ, flush на pagehide)
и восстанавливаются при открытии. В embed/онлайн-уроке персист выключен —
состоянием управляет учитель. applyState обёрнут в try/catch (старые формы не ломают).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-13 11:07:14 +03:00
parent 2067e6efb1
commit 51fcb6e4b7
2 changed files with 32 additions and 1 deletions
+31
View File
@@ -330,8 +330,39 @@ const SIMS = [
// Map simId → { getState, applyState } registered by openSim handlers
const _simStateRegistry = {};
/* ── Локальный персист параметров симуляции (Фаза 2) ──────────────────
Поверх того же getState/applyState: в обычном (не embed) режиме сохраняем
состояние активной симуляции в localStorage и восстанавливаем при открытии.
В embed/онлайн-уроке состоянием управляет учитель — персист отключён. */
const _LAB_STATE_KEY = 'lab-sim-state-v1';
function _loadSavedStates() { try { return JSON.parse(localStorage.getItem(_LAB_STATE_KEY) || '{}') || {}; } catch (e) { return {}; } }
function _saveSavedStates(m) { try { localStorage.setItem(_LAB_STATE_KEY, JSON.stringify(m)); } catch (e) {} }
let _persistSimId = null, _persistInterval = null, _lastPersisted = null;
function _stopPersist() { if (_persistInterval) { clearInterval(_persistInterval); _persistInterval = null; } _persistSimId = null; _lastPersisted = null; }
function _persistNow() {
if (!_persistSimId) return;
const reg = _simStateRegistry[_persistSimId];
if (!reg || !reg.getState) return;
try {
const s = reg.getState();
if (s == null) return;
const json = JSON.stringify(s);
if (json === _lastPersisted || json.length > 8000) return;
_lastPersisted = json;
const m = _loadSavedStates(); m[_persistSimId] = s; _saveSavedStates(m);
} catch (e) {}
}
function _startPersist(simId) { _stopPersist(); _persistSimId = simId; _persistInterval = setInterval(_persistNow, 2000); }
window.addEventListener('pagehide', _persistNow);
function _registerSimState(simId, getState, applyState) {
_simStateRegistry[simId] = { getState, applyState };
if (_embedMode) return; // в embed состоянием управляет учитель
// восстановить сохранённые параметры (после инициализации тела) + запустить персист
setTimeout(function () {
try { const saved = _loadSavedStates()[simId]; if (saved != null && applyState) applyState(saved); } catch (e) {}
_startPersist(simId);
}, 0);
}
let _lastEmittedState = null;