chore(sim-builder): план фичи (8 фаз) — конструктор симуляций
This commit is contained in:
@@ -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
|
||||
@@ -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`
|
||||
@@ -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
|
||||
<!-- Заполняет реализатор -->
|
||||
@@ -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:'<name>', 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
|
||||
<!-- Заполняет реализатор -->
|
||||
@@ -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
|
||||
<!-- Заполняет реализатор -->
|
||||
@@ -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
|
||||
<!-- Заполняет реализатор -->
|
||||
@@ -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
|
||||
<!-- Заполняет реализатор -->
|
||||
@@ -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:<dbid>`.
|
||||
- [ ] Карточки в каталоге: категория/предмет/класс из меты; бейдж «Моя»/«Опубликована».
|
||||
- [ ] Раздел/фильтр «Мои симуляции» в /lab.
|
||||
- [ ] Кнопки на карточке custom-sim: «Редактировать» → `sim-builder.html?id=<id>`, «Удалить» (владельцу).
|
||||
- [ ] Deep-link `/lab?sim=custom:<id>` открывает напрямую (расширить существующий `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
|
||||
<!-- Заполняет реализатор -->
|
||||
@@ -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:<id>`;
|
||||
чип «Связано с программой» (как у встроенных, `_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
|
||||
<!-- Заполняет реализатор -->
|
||||
@@ -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:<id>`; рантайм `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:<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
|
||||
- [ ] Все задачи выполнены
|
||||
- [ ] Синхрон параметров учитель→ученики работает
|
||||
- [ ] Доступ к custom-sim на доске проверяется
|
||||
- [ ] Встроенные sim на доске не сломаны; SimEngine корректно размонтируется
|
||||
|
||||
## Handoff to Next Phase
|
||||
<!-- Финальная фаза -->
|
||||
Reference in New Issue
Block a user