7.3 KiB
Phase 7: Доска онлайн-урока
Status: ✅ Implemented (pending commit) Parent plan: PLAN.md Domain: fullstack
Objective
Открывать custom-симуляцию на доске онлайн-урока через существующий конвейер встраивания sim, с синхроном параметров классу и аннотациями поверх.
Tasks
- Учитель в classroom выбирает sim для доски: добавить в список источников свои+published custom-sims (рядом со встроенными).
→
_crLoadCustomSims()(LS.customSimsList) + мёрж в_crRenderSimGrid; карточка с бейджем «Моя», id=custom:<dbid>. - Открытие на доске через существующий
simOpen(controllerclassroom/sim.js, роут/:id/sim) — для custom передаётсяcustom:<id>. Рантайм НЕ монтируется напрямую в доску: доска уже грузит sim в iframe/lab?embed=1&sim=...; для custom это/lab?embed=1&sim=custom:<id>, гдеLabCustom/registerSpecSimмонтируетSimEngine(путь Ф5). Конвейер iframe переиспользован 1:1. - Синхрон состояния: параметры + play/pause транслируются классу через существующий мост
sim_state/apply_sim_state(lab-glue) →simState. Custom-sim подключён к мосту через_bridgeCustomSimState(real)(getState/applyState поверхSimEngine.params/setParam/play/pause). ⚠️ Время t (фаза анимации) жёстко НЕ синхронится — только параметры слайдеров + признак running. - Аннотации поверх — через существующий
simAnnotate(без изменений конвейера, id-agnostic). - Закрытие/смена sim:
onSimCloseсбрасываетframe.src='about:blank'→ весь документ iframe (SimEngine + rAF + слушатели + реестр состояния) сносится вместе с iframe. Утечек нет. - Проверка: логическая (трасса teacher→backend→SSE→student) + node --check + npm test (в baseline). Headless classroom тяжёл; синхрон проверен по конвейеру, открытие/доступ/закрытие — по коду.
Files to Modify/Create
frontend/classroom.html— выбор custom-sim в источниках доски, монтаж SimEngine, проброс состояния (modify)backend/src/controllers/classroom/sim.js— поддержатьcustom:<id>(валидация доступа к published/own) (modify)js/api.js— при необходимости (modify, опц.)
Acceptance Criteria
- Учитель открывает custom-sim на доске; ученики видят её синхронно (параметры/анимация/режим).
- Аннотации поверх работают; закрытие чистит ресурсы.
- Существующее встраивание встроенных sim не регрессировало.
Notes
- Reuse simOpen/simState/simMode/simAnnotate — НЕ строить новый канал синхрона.
- Доступ: на доску можно класть только свои или published (проверка на сервере).
- classroom.html большой (8240 строк) — править точечно.
Review Checklist
- Все задачи выполнены
- Синхрон параметров учитель→ученики работает (sim_state мост, params+running)
- Доступ к custom-sim на доске проверяется (server simOpen: own|published|admin; иначе 403/404)
- Встроенные sim на доске не сломаны (id-ветка для встроенных без изменений); ресурсы чистятся через about:blank
Handoff to Next Phase
Handoff — итог Ф7 (степень синхрона, что осталось)
Изменённые файлы (минимально, аддитивно):
backend/src/controllers/classroom/sim.js(+21/-2):simOpenпринимаетcustom:<dbid>, валидирует доступ (владелец ИЛИ published ИЛИ admin → иначе 404/403). Встроенный id — старый regex.simState/simMode/simAnnotate/simCloseНЕ менялись (state-объект и так произвольный, ≤64KB).frontend/classroom.html(+31/-4):_crLoadCustomSims()(кэшLS.customSimsList),crOpenSimPickerasync + предзагрузка,_crRenderSimGridмёржит custom (бейдж «Моя», idcustom:<dbid>, XSS-escape заголовков/id в onclick).crPickSimуже передаёт id как есть.frontend/js/labs/lab-glue.js(+48/-1):_bridgeCustomSimState(real)— мост состояния для custom-sim; вызывается в_registerLazy.open()послеreal.open(ctx)(только embed). РегистрируетgetState={params,running}/applyState(setParam+play/pause) под ключом_autoSim(custom:<dbid>), запускает_startStateEmit. Тем же_simStateRegistry/каналом, что встроенные.
Степень синхрона: параметры слайдеров + признак воспроизведения (play/pause) — ПОЛНЫЙ синхрон
учитель→ученики (demo-режим). Время t (фаза анимации) НЕ синхронизируется покадрово: ученик
запускает свой rAF при running=true, фаза может разъезжаться. Достаточно по требованиям фазы.
При желании в будущем: добавить t в state и inst._t сеттер (потребует расширения SimEngine API).
Открытие на доске: через iframe /lab?embed=1&sim=custom:<id> (НЕ прямой mount в доску) —
переиспользован существующий конвейер; SimEngine монтируется уже внутри iframe (путь Ф5).
Доступ: двойная проверка — (1) simOpen на сервере при постановке на доску;
(2) GET /api/custom-sims/:id (ensureSpec) при загрузке спеки в iframe. Чужой draft → 403 на обоих.
Что осталось / риски:
- Нет интеграционного теста classroom-сессии (нет харнесса; добавление потребовало бы мока сессий и риска зайти в зону параллельной сессии). Проверка — логическая + node --check + npm test (в baseline).
- Время анимации не синхронится покадрово (см. выше) — by design.
- Остаток Ф6 (UI-редактор связей в билдере + чипы в каталоге) — вне Ф7.