Files
Learn_System/plans/sim-builder/phase-5-catalog.md
T

98 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 5: Каталог (custom-sims в /lab)
**Status:** ✅ Implemented (в рабочем дереве, не закоммичено — коммит за оркестратором)
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** fullstack
## Objective
Сохранённые custom-симуляции появляются и играют в /lab наравне со встроенными;
раздел «Мои симуляции», редактирование/удаление из каталога, deep-link.
## Tasks
- [x] При загрузке /lab подтягивать custom-sims (свои + published) из `GET /api/custom-sims`
(`LS.customSimsList()`) и регистрировать ЛЕНИВЫЕ манифесты в LabRegistry. spec НЕ грузится
на старте — тянется при первом открытии (`LS.customSimGet``registerSpecSim` Ф0-адаптера).
- [x] Карточки в каталоге: категория/предмет(grade)/из меты; бейдж «Моя» (owner) / «Опубликована»
(status) / «Черновик» (own draft).
- [x] Раздел «Мои симуляции» в /lab — отдельная секция `#custom-sim-section` в `#lab-home`
(создаётся динамически, без правок lab.html/CSS); уважает текущий фильтр категорий.
- [x] Кнопки на карточке custom-sim: «Редактировать» → `/sim-builder?id=<dbid>`, «Удалить»
(`LS.customSimDelete`) — только владельцу (`owner_id === user.id`).
- [x] Deep-link `/lab?sim=custom:<dbid>` открывает напрямую: хук `LabCustom.resolveId` в `openSim`
(lab-init.js) переводит `custom:<dbid>` → реестровый id `customsim_<dbid>`; для custom deep-link
открытие отложено до загрузки списка (и в обычном, и в embed-режиме).
- [x] Ленивая загрузка: движок (`_sim_expr/_sim_engine/_sim_adapter`) уже eager в lab.html (Ф0),
поэтому отдельный ленивый файл не нужен; лениво грузится только spec (тяжёлый JSON) при открытии.
`_sim_deps.js` НЕ тронут.
## 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
- [x] Все задачи выполнены
- [x] Встроенные симуляции и старт /lab не регрессировали (custom исключены из основной сетки
по флагу `_custom`; падение загрузки списка не ломает каталог — try/catch + мягкий warn)
- [x] Draft видит только владелец; published — все (видимость обеспечивает сервер Ф3:
`customSimsList` отдаёт свои любого статуса + чужие published; бейджи/кнопки — по `owner_id`)
- [x] Ленивая загрузка spec работает (кэш + дедуп; на старте спеки не грузятся)
## Handoff to Next Phase
### Что реализовано (Phase 5)
Только аддитивные правки двух файлов параллельной сессии — без рефактора их кода:
- **`frontend/js/labs/lab-init.js`** (+7 строк): в начало `openSim(id)` добавлен хук —
`if (window.LabCustom && LabCustom.resolveId) id = LabCustom.resolveId(id) || id;`
Переводит `custom:<dbid>` → реестровый id `customsim_<dbid>` (LabRegistry.get/has обрезают
часть после `:`, поэтому в реестре двоеточие недопустимо). Для встроенных id — no-op.
- **`frontend/js/labs/lab-glue.js`**:
- `renderSims()`: 1 строка в merge — `&& !m._custom` (custom-манифесты не попадают в основную
сетку встроенных) + вызов `LabCustom.renderSection(_catFilter)` после рендера грида.
- init-блок (non-embed): после `renderSims()` зовёт `LabCustom.init()`; для `?sim=custom:*`
открытие отложено до резолва списка. Аналогично в embed-ветке (на `load`).
- **`window.LabCustom`** (новый IIFE в конце файла): `init()` (fetch списка, регистрация
ленивых манифестов, рендер секции), `resolveId`, `renderSection`, `ensureSpec`, `del`.
### Как custom-sims попадают в каталог и открываются
1. `LS.customSimsList()` → мета (без spec). Для каждой — `_registerLazy(meta)`: в LabRegistry
кладётся манифест-заглушка `id='customsim_<dbid>'`, `_custom:true`, мета-поля, и `open()`,
который при первом вызове лениво тянет spec.
2. Секция «Мои симуляции» (`#custom-sim-section`) рендерится из `_meta` (НЕ из реестра): карточки
`.sim-card` с `data-open="custom:<dbid>"`, бейджами и (владельцу) кнопками edit/del. Делегат
кликов на секции: открыть / `/sim-builder?id=` / `LS.customSimDelete`.
3. Открытие: `openSim('custom:<dbid>')``resolveId``customsim_<dbid>` → дисп. реестра →
`open()` заглушки → `ensureSpec(dbid)` (`LS.customSimGet`, кэш+дедуп) → `spec.id=<regId>`
`registerSpecSim(spec)` (Ф0-адаптер строит реальный SimEngine-манифест, ЗАМЕНЯЕТ заглушку на
месте) → `setActive(real)` + `real.open(ctx)` (монтирует SimEngine). Повторное открытие —
синхронно, реальный манифест уже в реестре, spec из кэша.
### id-неймспейс
- Deep-link / клик / `data-open`: **`custom:<dbid>`**.
- LabRegistry / host: **`customsim_<dbid>`** (без `:`). Конвертация — только в `resolveId`.
### Формат карточки
preview-SVG (плейсхолдер) + cat-бейдж (`.sim-cat`) + бейджи «Моя»/«Опубликована»/«Черновик»
+ title + desc (+ «N класс») + (владельцу) ряд кнопок «Редактировать»/«Удалить» (inline SVG `.ic`).
### Риски / заметки для Ф6
- `_loadRelated('customsim_<dbid>')` дергает `/api/lab/sims/.../related` (404 для custom) — тихо
глотается. Если Ф6 заведёт связи custom-sim с программой — учесть этот id.
- Удаление: после `customSimDelete` карточка убирается из секции, но манифест-заглушка остаётся
в LabRegistry (LabRegistry не имеет unregister). Не критично (карточки нет, deep-link на
удалённую вернёт 404 при ensureSpec). Если Ф6/Ф7 потребуют чистку — добавить unregister в реестр.
- Ф6 (раздача/публикация/клон/шаблоны): кнопку «Поделиться/Раздать классу» добавлять в `_cardHtml`
(ещё один `data-act`); публикацию toggle — там же. Клон — новый `LS.customSimCreate` со spec
из `ensureSpec`. Источник spec для доски (Ф7) — `LabCustom.ensureSpec(dbid)` или живой
`LabRegistry.get('customsim_<dbid>').instance()`.