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:
@@ -330,8 +330,39 @@ const SIMS = [
|
|||||||
// Map simId → { getState, applyState } registered by openSim handlers
|
// Map simId → { getState, applyState } registered by openSim handlers
|
||||||
const _simStateRegistry = {};
|
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) {
|
function _registerSimState(simId, getState, applyState) {
|
||||||
_simStateRegistry[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;
|
let _lastEmittedState = null;
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
## Прогресс
|
## Прогресс
|
||||||
- [x] Фаза 0 (фундамент заложен) — эконом-режим/reduced-motion (LabFX, тумблер), выбор симуляции из списка в редакторе урока, удалён мёртвый `SimUtil`, добавлены `LabPalette` (_palette.js) и `SimBase` (_simbase.js) как опциональные основания. **Адаптация симуляций к SimBase/LabPalette и удаление «дробовика» `_pauseAllSims/closeSim` — постепенно, по мере правок каждой симуляции (требует поштучной проверки, нет фронт-тестов).**
|
- [x] Фаза 0 (фундамент заложен) — эконом-режим/reduced-motion (LabFX, тумблер), выбор симуляции из списка в редакторе урока, удалён мёртвый `SimUtil`, добавлены `LabPalette` (_palette.js) и `SimBase` (_simbase.js) как опциональные основания. **Адаптация симуляций к SimBase/LabPalette и удаление «дробовика» `_pauseAllSims/closeSim` — постепенно, по мере правок каждой симуляции (требует поштучной проверки, нет фронт-тестов).**
|
||||||
- [~] Фаза 1 — сделано: фреймворк `LabTasks` (_tasks.js) + интеграция в теорию; задания на 17 симуляций. Осталось: XP за задания, deep-link на §, наполнение остальных.
|
- [~] Фаза 1 — сделано: фреймворк `LabTasks` (_tasks.js) + интеграция в теорию; задания на 17 симуляций. Осталось: XP за задания, deep-link на §, наполнение остальных.
|
||||||
- [~] Фаза 2 — сделано: «Сохранить кадр в Мои материалы» + «Скачать PNG» в топбаре лаборатории (захват активного canvas, переиспользует MaterialSave.image). Осталось: сохранение/возобновление параметров симуляции, измерительные инструменты. (3D/WebGL-кадр может быть пустым без preserveDrawingBuffer — доработать.)
|
- [~] Фаза 2 — сделано: «Сохранить кадр в Мои материалы» + «Скачать PNG»; сохранение/возобновление параметров симуляции (localStorage поверх getState/applyState, не в embed). Осталось: измерительные инструменты (линейка/транспортир). (3D/WebGL-кадр пустой без preserveDrawingBuffer — доработать.)
|
||||||
- [ ] Фаза 3
|
- [ ] Фаза 3
|
||||||
- [ ] Фаза 4
|
- [ ] Фаза 4
|
||||||
- [ ] Фаза 5
|
- [ ] Фаза 5
|
||||||
|
|||||||
Reference in New Issue
Block a user