diff --git a/plans/sim-builder/CONTEXT.md b/plans/sim-builder/CONTEXT.md new file mode 100644 index 0000000..0430b1e --- /dev/null +++ b/plans/sim-builder/CONTEXT.md @@ -0,0 +1,42 @@ +# Feature Context: Конструктор симуляций (SimForge) + +## Current State +- Фаза 0 не начата. Ветка `feature/sim-builder` от `master`. +- Лаборатория уже декларативна на уровне регистрации: `frontend/js/labs/_registry.js` + (`LabRegistry.register/get/all/setActive/stop/destroy/resolvePreview`), манифест с + `open(ctx)/mount(host)/stop/destroy`. ~40 симуляций — рукописные JS-модули в `frontend/js/labs/`. +- Каталог в БД: миграция `042_lab_sims.sql` (`lab_sims`), роуты `backend/src/routes/lab.js` + (`GET /api/lab/sims`, PATCH/:id, POST /reorder, links). Привязка к программе: `043_lab_sim_links.sql`. + +## Архитектурные решения (зафиксированы при планировании) +- **Спека = JSON-данные.** Версия `specVersion`. Корень: `{ specVersion, meta, viewport, params[], objects[], physics?, plots[], controls }`. +- **Движок выражений безопасный** — собственный парсер (расширение `y=f(x)` из graph.js): + токенайзер → AST → eval по окружению `{ params, t, объекты, whitelisted Math fns }`. + ⛔ Без `eval`/`Function`. Whitelist: + - * / ^ %, sin cos tan asin acos atan sqrt abs exp ln log min max floor ceil round sign pi e, сравнения, ?:. +- **Рантайм** `window.SimEngine.mount(host, spec) -> instance{ play, pause, reset, setParam, destroy }`. + Рендер: canvas для геометрии/трасс + SVG/absolute-div оверлей для подписей (KaTeX). + Регистрируется в LabRegistry адаптером (одна функция строит манифест из спеки). +- **Объект**: `{ id, type, ...props-with-bindings }`. type ∈ point|segment|vector|circle|rect|polyline|path|label|image. Любое числовое свойство может быть числом ИЛИ строкой-выражением. +- **Физический режим (Фаза 2)**: объект с `body:{ mass, vx, vy, fixed }` интегрируется `_fx_motion`; силы `physics:{ gravity, springs[], collisions, friction, walls }`. Формульный и физический режимы сосуществуют (формульные объекты — кинематические). +- **Безопасность шаринга**: published-спека валидируется на сервере (размер, схема, глубина AST, число объектов/параметров); подписи-строки санитизируются как svg/текст. + +## Temporary Workarounds +- (нет) + +## Cross-Phase Dependencies +- Ф1 (графики/drag) зависит от рантайма Ф0. +- Ф2 (физика) зависит от Ф0 (модель объектов/цикл). +- Ф4 (билдер) зависит от Ф0–Ф2 (что строить) + Ф3 (куда сохранять). +- Ф5 (каталог) зависит от Ф3 (БД) + Ф0 (адаптер LabRegistry). +- Ф6 (раздача) зависит от Ф3+Ф5. +- Ф7 (доска) зависит от Ф0 (рантайм) + Ф5 (источник sim) + существующего `simOpen/simState`. + +## Implementation Notes +- Каждая фаза должна оставлять /lab рабочим (Incremental). +- Тестировать рантайм Ф0–Ф2 рукописными спеками-фикстурами (без билдера). +- Reuse > переписывание: сначала смотреть `_fx_motion`, `_graph_panel`, `graph.js`. + +## RESUME STATE +- Последний коммит фичи: — (ещё нет) +- Текущая фаза: Phase 0 — Runtime core (Not Started) +- Режим: Automated / Orchestrator / Incremental diff --git a/plans/sim-builder/PLAN.md b/plans/sim-builder/PLAN.md new file mode 100644 index 0000000..0e60784 --- /dev/null +++ b/plans/sim-builder/PLAN.md @@ -0,0 +1,68 @@ +# Feature: Конструктор симуляций (SimForge) + +**Branch:** `feature/sim-builder` +**Base branch:** `master` +**Created:** 2026-06-13 +**Status:** 🟡 In Progress +**Strategy:** Incremental +**Mode:** Automated +**Execution:** Orchestrator + +## Summary +Полноценный движок авторинга интерактивных 2D-симуляций для учителя-непрограммиста. +Учитель собирает симуляцию из **данных** (JSON-спека): параметры-слайдеры, объекты +(фигуры/векторы/точки/подписи с LaTeX), привязанные **формулами** к параметрам и времени +`t`; настоящая физика (гравитация/пружины/столкновения/трение); графики; перетаскивание. +Сохраняет в БД, публикует в каталог лаборатории, раздаёт классу, открывает на доске +онлайн-урока, клонирует чужие и стартует из шаблонов. + +Спека — это **данные, не код**. Движок выражений — безопасный (whitelisted-математика), +⛔ без `eval`/`Function`/доступа к DOM/глобалам: спека шарится между людьми. + +## Build & Test Commands +- **Build:** нет (vanilla JS, без бандлера) +- **Test:** `npm test` (в `backend/`, `node --test tests/*.test.js`) +- **Lint:** `npm run lint:routes` (в `backend/`) +- ⚠️ После роутов/миграций: `npm run migrate` (живая БД) + рестарт сервера (авто-перезагрузки нет). +- ⚠️ `npm test` имеет baseline 3 pre-existing fail (auth.test.js) — хук толерантен (BASELINE_FAILS=3). + +## Project Constraints (соблюдают ВСЕ агенты) +- ⛔ Никаких эмодзи в коде — только inline SVG `.ic`. +- Поиск по коду: `ast-index` (символы/usages/callers) + `vex` (semantic). НЕ Grep tool. +- БД — встроенный `node:sqlite` (`DatabaseSync`), НЕ better-sqlite3. Живая БД `backend/data/learnspace.db`. +- Frontend — vanilla JS, `window.LS.*` (js/api.js), без бандлера. Статика через Express. +- Стейджить файлы поимённо (НЕ `git add -A` — в репо много мусорных untracked). +- Движок выражений — безопасный парсер, не `eval`/`new Function`. +- Переиспользовать: LabRegistry, `_fx_motion`, `_graph_panel`, `_phys_visuals`, `_util`, + парсер `y=f(x)` из graph.js; паттерн раздачи из «Мои материалы»; `lab_sim_links`; + конвейер встраивания sim на доску (`simOpen/simState/simMode/simAnnotate`). + +## Phases + +- [ ] Phase 0: Спека v1 + рантайм (формульные сцены) [domain: frontend] → [subplan](./phase-0-runtime-core.md) +- [ ] Phase 1: Графики + интеракции [domain: frontend] → [subplan](./phase-1-plots-interactions.md) +- [ ] Phase 2: Физический интегратор [domain: frontend] → [subplan](./phase-2-physics.md) +- [ ] Phase 3: БД + API (custom_sims) [domain: backend] → [subplan](./phase-3-persistence-api.md) +- [ ] Phase 4: Билдер (редактор) [domain: frontend] → [subplan](./phase-4-builder-ui.md) +- [ ] Phase 5: Каталог (custom-sims в /lab) [domain: fullstack] → [subplan](./phase-5-catalog.md) +- [ ] Phase 6: Раздача / шаблоны / клон / программа [domain: fullstack] → [subplan](./phase-6-sharing.md) +- [ ] Phase 7: Доска онлайн-урока [domain: fullstack] → [subplan](./phase-7-classroom.md) + +## Phase Progress Log + +| Phase | Domain | Status | Review | Build | Committed | +|-------|--------|--------|--------|-------|-----------| +| Phase 0: Runtime core | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ | +| Phase 1: Plots & interactions | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ | +| Phase 2: Physics | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ | +| Phase 3: Persistence + API | backend | ⬜ Not Started | ⬜ | ⬜ | ⬜ | +| Phase 4: Builder UI | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ | +| Phase 5: Catalog | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ | +| Phase 6: Sharing | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ | +| Phase 7: Classroom | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ | + +## Final Review +- [ ] Comprehensive code review (final-reviewer) +- [ ] Security review (safe expression eval, ownership, sanitization) +- [ ] Full test suite passes (within baseline) +- [ ] Merged to `master` diff --git a/plans/sim-builder/phase-0-runtime-core.md b/plans/sim-builder/phase-0-runtime-core.md new file mode 100644 index 0000000..6bc6fdf --- /dev/null +++ b/plans/sim-builder/phase-0-runtime-core.md @@ -0,0 +1,48 @@ +# Phase 0: Спека v1 + рантайм (формульные сцены) + +**Status:** ⬜ Not Started +**Parent plan:** [PLAN.md](./PLAN.md) +**Domain:** frontend + +## Objective +Заложить ядро: формат JSON-спеки v1, безопасный движок выражений, рантайм `SimEngine`, +адаптер регистрации в `LabRegistry`. После фазы рукописная спека «брошенное тело» играет в /lab. + +## Tasks +- [ ] Задокументировать формат спеки v1 в шапке нового файла + в CONTEXT.md (params, objects, viewport, controls). +- [ ] `frontend/js/labs/_sim_expr.js` — безопасный движок выражений: токенайзер → AST → `evaluate(ast, env)`. Whitelist математики (см. CONTEXT.md). Парсер расширяет логику `y=f(x)` из `graph.js` (посмотреть и переиспользовать, не дублировать). ⛔ без `eval`/`Function`. `compile(src) -> {fn(env), error}`. +- [ ] `frontend/js/labs/_sim_engine.js` — `window.SimEngine.mount(host, spec)`: + - создаёт canvas (мир→экран трансформация по `viewport`) + оверлей-слой для подписей (KaTeX через существующий путь рендера формул); + - объекты: point|segment|vector|circle|rect|polyline|path|label (числовые свойства = число или строка-выражение, компилируются один раз); + - игровой цикл `requestAnimationFrame`: пересчёт `t`, перевычисление привязок, перерисовка; + - контролы: слайдеры-параметры (из `params[]`), кнопки play/pause/reset; API инстанса `{ play, pause, reset, setParam, destroy }`. +- [ ] `frontend/js/labs/_sim_adapter.js` — `registerSpecSim(spec)` строит манифест LabRegistry (`open(ctx)` → `SimEngine.mount`, `stop/destroy`, `preview` из спеки) и регистрирует. +- [ ] Фикстура-демо: рукописная спека «projectile» (угол/скорость слайдеры, точка x=v·cosθ·t, y=v·sinθ·t−5t²) — зарегистрировать как `customdemo` для проверки в /lab (за временным флагом/разделом, не светить ученикам). +- [ ] Подключить новые файлы в /lab (lazy через существующий `_loader`/`_sim_deps` или прямыми тегами — выбрать минимально-инвазивно, не ломая старт). + +## Files to Modify/Create +- `frontend/js/labs/_sim_expr.js` — движок выражений (new) +- `frontend/js/labs/_sim_engine.js` — рантайм (new) +- `frontend/js/labs/_sim_adapter.js` — адаптер LabRegistry (new) +- `frontend/js/labs/_sim_demo.js` — демо-спека-фикстура (new, временная) +- `frontend/lab.html` или `_sim_deps.js` — подключение файлов (минимальная правка) + +## Acceptance Criteria +- В /lab открывается демо-симуляция, слайдеры меняют движение, play/pause/reset работают. +- Движок выражений не использует eval/Function; некорректная формула не роняет рантайм (показывает ошибку/0). +- Существующие ~40 симуляций и старт /lab не сломаны. + +## Notes +- Подписи с LaTeX — переиспользовать существующий рендер формул (KaTeX), не тянуть новый. +- Мир-координаты с осью Y вверх (математические), трансформация в экранные внутри движка. +- Производительность: компилировать выражения один раз при mount, в цикле только evaluate. + +## Review Checklist +- [ ] Все задачи выполнены +- [ ] Нет eval/new Function в движке выражений +- [ ] Нет эмодзи (только SVG .ic) +- [ ] Старт /lab и существующие симуляции не регрессировали +- [ ] Код в стиле проекта (vanilla, IIFE-модули labs/) + +## Handoff to Next Phase + diff --git a/plans/sim-builder/phase-1-plots-interactions.md b/plans/sim-builder/phase-1-plots-interactions.md new file mode 100644 index 0000000..3b75e9a --- /dev/null +++ b/plans/sim-builder/phase-1-plots-interactions.md @@ -0,0 +1,40 @@ +# Phase 1: Графики + интеракции + +**Status:** ⬜ Not Started +**Parent plan:** [PLAN.md](./PLAN.md) +**Domain:** frontend + +## Objective +Добавить в рантайм графики (plot-объекты), перетаскиваемые ручки (drag → параметр), +векторы и числовые readout. После фазы спека со слайдером, draggable-точкой и live-графиком работает. + +## Tasks +- [ ] Plot-объект в спеке: `{ type:'plot', expr:'...', var:'x', range:[a,b], samples, trace? }` — + рисует график выражения; `trace:true` — накапливает след по `t`. Переиспользовать `_graph_panel.js` (посмотреть API). +- [ ] Draggable-ручка: объект/маркер с `drag:{ param:'', axis:'x|y|xy', min,max }` — перетаскивание мышью/тачем меняет параметр (и наоборот, позиция следует за параметром). Хит-тест в мир-координатах. +- [ ] Readout: `{ type:'readout', label, expr, unit, precision }` — живое значение выражения как текст/бейдж. +- [ ] Vector-объект с привязкой к (origin, dx, dy)-выражениям + стрелка. +- [ ] Тач-поддержка drag (pointer events), не ломая существующую логику доски/лабы. +- [ ] Обновить демо-спеку: добавить слайдер, draggable-точку, plot + readout. + +## Files to Modify/Create +- `frontend/js/labs/_sim_engine.js` — типы plot/readout/vector, drag-интеракции (modify) +- `frontend/js/labs/_sim_demo.js` — расширить демо (modify) + +## Acceptance Criteria +- Перетаскивание ручки меняет параметр; зависимые объекты/график обновляются. +- График строится по выражению; trace накапливает след во времени. +- Readout показывает живое значение. Тач работает. + +## Notes +- Drag не должен конфликтовать с pan/zoom рантайма (если есть). Приоритет хит-теста — ручки. +- Сэмплинг графика разумный (без фриза на больших range). + +## Review Checklist +- [ ] Все задачи выполнены +- [ ] Drag работает мышью и тачем +- [ ] Нет регрессий рантайма Ф0 +- [ ] Нет эмодзи, стиль проекта + +## Handoff to Next Phase + diff --git a/plans/sim-builder/phase-2-physics.md b/plans/sim-builder/phase-2-physics.md new file mode 100644 index 0000000..7d65988 --- /dev/null +++ b/plans/sim-builder/phase-2-physics.md @@ -0,0 +1,42 @@ +# Phase 2: Физический интегратор + +**Status:** ⬜ Not Started +**Parent plan:** [PLAN.md](./PLAN.md) +**Domain:** frontend + +## Objective +Добавить настоящую физику: тела с массой, гравитация/пружины/столкновения/трение, +перетаскивание тел силой, траектории. Динамика считается движком, а не формулой. +После фазы маятник/столкновения/брошенное тело идут динамически из спеки. + +## Tasks +- [ ] Блок `physics` в спеке: `{ enabled, gravity:{x,y}, friction, walls:[...], restitution }`. +- [ ] Тело-объект: `body:{ mass, vx, vy, fixed }` — интегрируется (опора на `_fx_motion.js`, посмотреть API; не дублировать интегратор). +- [ ] Пружины: `springs:[{ a, b, k, length }]` (между телами или телом и точкой-якорем). +- [ ] Столкновения: упругие шары/стены (restitution), базовый бродфейз достаточно (N небольшое). +- [ ] Drag тела: перетаскивание задаёт позицию/скорость (отпустил — летит). Кинематические (формульные) объекты Ф0 сосуществуют с физическими. +- [ ] Траектория: накопление следа центра тела (toggle в спеке). +- [ ] Демо-спеки: «маятник» (груз+нить как пружина/констрейнт), «упругие шары». + +## Files to Modify/Create +- `frontend/js/labs/_sim_engine.js` — физический режим, интеграция с _fx_motion (modify) +- `frontend/js/labs/_sim_physics.js` — обёртка интегратора/коллизий, если чище отдельно (new, опц.) +- `frontend/js/labs/_sim_demo.js` — физ-демо (modify) + +## Acceptance Criteria +- Тело под гравитацией падает/летит по параболе через интегратор (не по формуле). +- Пружина колеблет груз; шары упруго сталкиваются; стены отражают. +- Drag тела работает; формульные объекты Ф0 продолжают работать в той же сцене. + +## Notes +- Шаг интегратора фиксированный (накопитель dt) для стабильности. +- Не переусложнять коллизии — школьный уровень (круги/стены). + +## Review Checklist +- [ ] Все задачи выполнены +- [ ] Использует _fx_motion, без своего дубля интегратора без причины +- [ ] Стабильность (нет взрыва энергии на разумных параметрах) +- [ ] Нет регрессий Ф0/Ф1 + +## Handoff to Next Phase + diff --git a/plans/sim-builder/phase-3-persistence-api.md b/plans/sim-builder/phase-3-persistence-api.md new file mode 100644 index 0000000..34a203e --- /dev/null +++ b/plans/sim-builder/phase-3-persistence-api.md @@ -0,0 +1,51 @@ +# Phase 3: БД + API (custom_sims) + +**Status:** ⬜ Not Started +**Parent plan:** [PLAN.md](./PLAN.md) +**Domain:** backend + +## Objective +Сохранение custom-симуляций: таблица БД, CRUD API под авторизацией с проверкой владения, +серверная валидация спеки. После фазы спека сохраняется/грузится/удаляется через API. + +## Tasks +- [ ] Миграция `backend/src/db/migrations/0NN_custom_sims.sql` (следующий свободный номер): + таблица `custom_sims` (id, owner_id FK users ON DELETE CASCADE, title, description, subject, + grade, cat, spec_json TEXT, status TEXT 'draft|published' DEFAULT 'draft', version INT, + created_at, updated_at) + индекс по owner_id, status. +- [ ] Контроллер `backend/src/controllers/customSimController.js`: list (own + published), get, + create, update, remove. Владение проверяется на mutate (owner или admin). +- [ ] Серверная валидация спеки `validateSpec(spec)`: размер JSON, `specVersion`, лимиты + (число params/objects, глубина строк-выражений), типы объектов из whitelist, строки-подписи + обрезаются/санитизируются. Отказ с 400 при нарушении. ⛔ Никакого исполнения спеки на сервере. +- [ ] Роуты `backend/src/routes/customSims.js`: `GET /api/custom-sims` (свои+published), + `GET /api/custom-sims/:id`, `POST /` (teacher/admin), `PUT /:id`, `DELETE /:id`. Смонтировать в server.js под authMiddleware + фича-гейт при необходимости. +- [ ] Клиент `js/api.js`: `customSimsList/Get/Create/Update/Delete` + добавить в `window.LS`. +- [ ] Тесты `backend/tests/custom-sims.test.js`: CRUD, ownership (403 чужой), валидация (400 кривая спека). Использовать `seedRow()` паттерн (устойчив к дрейфу схемы). + +## Files to Modify/Create +- `backend/src/db/migrations/0NN_custom_sims.sql` (new) +- `backend/src/controllers/customSimController.js` (new) +- `backend/src/routes/customSims.js` (new) +- `backend/src/server.js` — монтирование роутера (modify) +- `js/api.js` — клиентские методы (modify) +- `backend/tests/custom-sims.test.js` (new) + +## Acceptance Criteria +- POST сохраняет спеку, GET возвращает свои+published, PUT/DELETE только владельцу/админу (403 иначе). +- Кривая/огромная спека → 400. Тесты зелёные (в пределах baseline). +- `npm run migrate` применяет таблицу; роут не отдаёт SPA-fallback после рестарта. + +## Notes +- node:sqlite (DatabaseSync), НЕ better-sqlite3. +- Спека хранится как TEXT(JSON); парс/валидация — на входе. +- Не делать blanket `router.use(requireRole('admin'))` — read-роуты auth-only, мутации — inline requireRole. + +## Review Checklist +- [ ] Все задачи выполнены +- [ ] Ownership и валидация спеки покрыты тестами +- [ ] Миграция идемпотентна (IF NOT EXISTS / INSERT OR IGNORE где надо) +- [ ] `npm run lint:routes` чисто; тесты в пределах baseline + +## Handoff to Next Phase + diff --git a/plans/sim-builder/phase-4-builder-ui.md b/plans/sim-builder/phase-4-builder-ui.md new file mode 100644 index 0000000..5f0206c --- /dev/null +++ b/plans/sim-builder/phase-4-builder-ui.md @@ -0,0 +1,46 @@ +# Phase 4: Билдер (редактор) + +**Status:** ⬜ Not Started +**Parent plan:** [PLAN.md](./PLAN.md) +**Domain:** frontend + +## Objective +Учительский редактор: страница с живым превью и панелями для сборки спеки без кода. +После фазы учитель собирает рабочую симуляцию с нуля в UI и сохраняет в БД (Ф3). + +## Tasks +- [ ] Страница `frontend/sim-builder.html` (доступ teacher/admin; сайдбар как у других страниц). +- [ ] Левая/правая панель + центр-превью (встроенный `SimEngine` инстанс, перемонтаж при правках). +- [ ] Панель **Параметры**: добавить/удалить параметр (имя, min, max, step, начальное, единица) → слайдер в превью. +- [ ] Панель **Объекты**: добавить объект (тип из whitelist), редактор свойств; числовые поля + принимают число ИЛИ выражение; палитра-помощник функций/параметров; подпись с LaTeX. +- [ ] Панель **Графики/Физика**: добавить plot (expr/var/range/trace); тумблер физики + её поля (Ф2). +- [ ] Размещение объектов мышью на превью (клик-поставить, drag-двигать) с синхроном в свойства. +- [ ] Мета: заголовок, описание, предмет, класс, категория, превью-картинка (опц.). +- [ ] Save/Load через `LS.customSims*` (Ф3): новый / редактировать существующий; кнопка «Тест» (play inline). +- [ ] Валидация на клиенте (понятные ошибки до сохранения) + показ ошибок выражений. + +## Files to Modify/Create +- `frontend/sim-builder.html` (new) — страница + инлайн-логика редактора +- `frontend/js/labs/_sim_engine.js` — при необходимости hook'и для билдера (live re-mount, highlight) (modify, минимально) +- `js/api.js` — если нужны доп. методы (modify, опц.) +- ссылка в сайдбаре/навигации для учителя (modify соответствующего include) + +## Acceptance Criteria +- Учитель с нуля добавляет параметры/объекты/график, видит живое превью, сохраняет, открывает заново и видит то же. +- Ошибка в выражении показывается понятно, не роняет редактор. +- Нет эмодзи, дизайн в системе ls.css. + +## Notes +- Прагматично: форма-панели + лёгкий drag на превью. НЕ полноценный node-граф. +- Это frontend-фаза — использовать гайдлайны frontend-design. +- Большой файл — держать логику модульной (можно вынести в `frontend/js/sim-builder.js`). + +## Review Checklist +- [ ] Все задачи выполнены +- [ ] Полный цикл build→save→reload→edit работает +- [ ] Доступ только teacher/admin +- [ ] Нет эмодзи; дизайн-система соблюдена + +## Handoff to Next Phase + diff --git a/plans/sim-builder/phase-5-catalog.md b/plans/sim-builder/phase-5-catalog.md new file mode 100644 index 0000000..5c1f801 --- /dev/null +++ b/plans/sim-builder/phase-5-catalog.md @@ -0,0 +1,42 @@ +# Phase 5: Каталог (custom-sims в /lab) + +**Status:** ⬜ Not Started +**Parent plan:** [PLAN.md](./PLAN.md) +**Domain:** fullstack + +## Objective +Сохранённые custom-симуляции появляются и играют в /lab наравне со встроенными; +раздел «Мои симуляции», редактирование/удаление из каталога, deep-link. + +## Tasks +- [ ] При загрузке /lab подтягивать custom-sims (свои + published) из `GET /api/custom-sims` + и регистрировать через `registerSpecSim` (Ф0-адаптер) с id `custom:`. +- [ ] Карточки в каталоге: категория/предмет/класс из меты; бейдж «Моя»/«Опубликована». +- [ ] Раздел/фильтр «Мои симуляции» в /lab. +- [ ] Кнопки на карточке custom-sim: «Редактировать» → `sim-builder.html?id=`, «Удалить» (владельцу). +- [ ] Deep-link `/lab?sim=custom:` открывает напрямую (расширить существующий `LAB_SIM_ALIASES`/openSim). +- [ ] Ленивая загрузка движка (`_sim_*.js`) — только когда открыта custom-sim (через `_loader`/`_sim_deps`). + +## Files to Modify/Create +- `frontend/js/labs/lab-glue.js` и/или `lab-init.js` — загрузка+регистрация custom-sims, карточки, фильтр (modify) +- `frontend/js/labs/_sim_deps.js` — `_sim_*.js` в ленивые зависимости (modify) +- `js/api.js` — при необходимости (modify, опц.) + +## Acceptance Criteria +- Сохранённая в Ф4 симуляция видна в /lab, открывается и играет. +- «Мои симуляции» показывает свои (вкл. draft); published видят и другие. +- Edit/Delete с карточки работают; deep-link открывает. +- Старт /lab не тормозит (движок грузится лениво). + +## Notes +- НЕ ломать существующий каталог встроенных (lab_sims) — custom-список добавляется поверх. +- id-неймспейс `custom:` чтобы не конфликтовать со встроенными. + +## Review Checklist +- [ ] Все задачи выполнены +- [ ] Встроенные симуляции и старт /lab не регрессировали +- [ ] Draft видит только владелец; published — все +- [ ] Ленивая загрузка движка работает + +## Handoff to Next Phase + diff --git a/plans/sim-builder/phase-6-sharing.md b/plans/sim-builder/phase-6-sharing.md new file mode 100644 index 0000000..7e8fa2a --- /dev/null +++ b/plans/sim-builder/phase-6-sharing.md @@ -0,0 +1,47 @@ +# Phase 6: Раздача / шаблоны / клон / программа + +**Status:** ⬜ Not Started +**Parent plan:** [PLAN.md](./PLAN.md) +**Domain:** fullstack + +## Objective +Экосистема вокруг custom-sims: публикация, раздача классу, клонирование чужих, +старт из шаблонов, привязка к программе (учебник/тема). + +## Tasks +- [ ] Публикация: тумблер draft↔published в билдере/каталоге (PUT status). Только владелец/админ. +- [ ] Раздача классу: `POST /api/custom-sims/:id/share { classId }` — по паттерну «Мои материалы» + (`shareMaterial`): ученики класса получают доступ/уведомление. Решить — ссылка-доступ или копия + (рекоменд.: доступ-ссылка на published + запись в lab_sim_links/доступ; копия избыточна). +- [ ] Клон: `POST /api/custom-sims/:id/clone` — копия спеки новому владельцу (draft). Кнопка «Клонировать» на чужой published-карточке. +- [ ] Шаблоны: набор стартовых спек (встроенные JSON-фикстуры: пустая, маятник, график, бросок) → + «Создать из шаблона» в билдере; «Создать из существующей» = clone. +- [ ] Привязка к программе: переиспользовать `lab_sim_links` (kind=textbook|topic) для `custom:`; + чип «Связано с программой» (как у встроенных, `_loadRelated`) и кнопка «В лабораторию» с карточки учебника. +- [ ] Тесты: share (доступ ученику), clone (новый владелец, draft), ownership на публикации. + +## Files to Modify/Create +- `backend/src/controllers/customSimController.js` — share/clone/publish (modify) +- `backend/src/routes/customSims.js` — роуты share/clone (modify) +- `backend/src/controllers/.../lab links` — связи для custom (reuse `lab.js` links, modify при необходимости) +- `frontend/sim-builder.html` / `frontend/js/labs/lab-glue.js` — шаблоны, кнопки публикации/клона/раздачи (modify) +- `js/api.js` — методы share/clone (modify) +- `backend/tests/custom-sims-share.test.js` (new) + +## Acceptance Criteria +- Учитель публикует; другой учитель видит и клонирует к себе (draft). +- Выданная классу симуляция доступна ученикам класса (с уведомлением). +- Старт из шаблона создаёт рабочую заготовку. Привязка к учебнику показывает чип/кнопку. + +## Notes +- Раздача — переиспользовать существующий механизм доступа/уведомлений, не строить новый. +- Решение копия-vs-ссылка зафиксировать в CONTEXT.md. + +## Review Checklist +- [ ] Все задачи выполнены +- [ ] Ownership на publish/share/clone покрыт тестами +- [ ] Ученик класса получает доступ; чужой — нет +- [ ] Reuse материалов/доступа/links, без дублей + +## Handoff to Next Phase + diff --git a/plans/sim-builder/phase-7-classroom.md b/plans/sim-builder/phase-7-classroom.md new file mode 100644 index 0000000..f334974 --- /dev/null +++ b/plans/sim-builder/phase-7-classroom.md @@ -0,0 +1,43 @@ +# Phase 7: Доска онлайн-урока + +**Status:** ⬜ Not Started +**Parent plan:** [PLAN.md](./PLAN.md) +**Domain:** fullstack + +## Objective +Открывать custom-симуляцию на доске онлайн-урока через существующий конвейер встраивания +sim, с синхроном параметров классу и аннотациями поверх. + +## Tasks +- [ ] Учитель в classroom выбирает sim для доски: добавить в список источников свои+published custom-sims (рядом со встроенными). +- [ ] Открытие на доске через существующий `simOpen` (controller `classroom/sim.js`, роут `/:id/sim`) — + для custom передаётся `custom:`; рантайм `SimEngine` монтируется в sim-контейнер доски. +- [ ] Синхрон состояния: параметры/play-pause/время транслировать классу через `simState/simMode` + (как для встроенных) — ученики видят те же значения слайдеров и фазу анимации. +- [ ] Аннотации поверх — через существующий `simAnnotate` (без изменений конвейера). +- [ ] Закрытие/смена sim корректно размонтирует `SimEngine` (destroy). +- [ ] Тест/проверка: открыть custom-sim на доске, подвигать слайдер у учителя → у ученика синхрон. + +## Files to Modify/Create +- `frontend/classroom.html` — выбор custom-sim в источниках доски, монтаж SimEngine, проброс состояния (modify) +- `backend/src/controllers/classroom/sim.js` — поддержать `custom:` (валидация доступа к published/own) (modify) +- `js/api.js` — при необходимости (modify, опц.) + +## Acceptance Criteria +- Учитель открывает custom-sim на доске; ученики видят её синхронно (параметры/анимация/режим). +- Аннотации поверх работают; закрытие чистит ресурсы. +- Существующее встраивание встроенных sim не регрессировало. + +## Notes +- Reuse simOpen/simState/simMode/simAnnotate — НЕ строить новый канал синхрона. +- Доступ: на доску можно класть только свои или published (проверка на сервере). +- classroom.html большой (8240 строк) — править точечно. + +## Review Checklist +- [ ] Все задачи выполнены +- [ ] Синхрон параметров учитель→ученики работает +- [ ] Доступ к custom-sim на доске проверяется +- [ ] Встроенные sim на доске не сломаны; SimEngine корректно размонтируется + +## Handoff to Next Phase +