chore(memory): снимок файлов памяти Claude в репозиторий для переноса
Копия пользовательской автопамяти (29 фактов + индекс MEMORY.md) в .claude/memory/, чтобы переносить между машинами через git. README.md — как восстановить в пользовательскую папку на другой машине. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# LearnSpace Project Memory
|
||||
|
||||
- [project_status.md](project_status.md) — Полный список реализованных фич: все страницы, API, таблицы БД, инструменты доски, стек, деплой (апрель 2026)
|
||||
- [project_classroom_module.md](project_classroom_module.md) — Оригинальный план classroom-модуля (4 фазы, все реализованы)
|
||||
- [project_whiteboard_roadmap.md](project_whiteboard_roadmap.md) — Roadmap улучшений доски (7 фаз, утверждён 2026-04-11)
|
||||
- [project_pet_assistant.md](project_pet_assistant.md) — «Квантик-ассистент» РЕАЛИЗОВАН (commit 3f8009c): Ф0/Ф1/«Спроси-FAQ», правиловый движок, reuse 'pet', assistant_seen, на учебнике тоже. НЕ сделано: Ф2 тур, реальная LLM, activeLesson
|
||||
- [feedback_no_emoji.md](feedback_no_emoji.md) — Запрет эмоджи в коде, только inline SVG `.ic`
|
||||
- [feedback_sims_admin_sync.md](feedback_sims_admin_sync.md) — При добавлении симуляции в lab.html → сразу обновить ADMIN_SIMS в admin.html
|
||||
- [project_ct_seeded.md](project_ct_seeded.md) — Список перенесённых сборников ЦТ/ЦЭ (физика 2024 + матем 2024); правило: 1 вариант из сборника, нет повторов
|
||||
- [project_hardening_2026.md](project_hardening_2026.md) — 8-task security/architecture hardening plan (started 2026-05-06), executed by Sonnet sessions one task at a time
|
||||
- [reference_textbook_sources.md](reference_textbook_sources.md) — Расположение PDF учебников Беларуси (физика/алгебра/геометрия 7-11) в `G:\Dev\Тесты\Методички\тест_6 класс\Книги\` + структура §-канвы Исаченковой
|
||||
- [project_stereo3d_improvements.md](project_stereo3d_improvements.md) — Стереометрия 3D: апгрейд 5 фаз (май 2026) + deep-link фигур `openSim('stereo:<figure>')` / `?stereofig=`
|
||||
- [reference_sqlite_node.md](reference_sqlite_node.md) — БД на встроенном node:sqlite (НЕ better-sqlite3); живая БД backend/data/learnspace.db; Bash ломает кириллический путь
|
||||
- [reference_textbook_latex_escaping.md](reference_textbook_latex_escaping.md) — Баг формул = ЛИШНИЕ слэши (over-escape), правило чётности, фикс fix_overescaped_latex.js; БД чиста
|
||||
- [project_content_access.md](project_content_access.md) — Доступ к учебникам/экзаменам/симуляциям/курсам по классам и ученикам (allowlist, ученик > класс), миграции 040/051/052, /api/access; ревью+переработка done, Фаза 3 (HTML-гейт) отложена
|
||||
- [project_permissions_rework.md](project_permissions_rework.md) — Ролевые права (registry/role_permissions/user_permissions): Phase A+B+C ВСЕ в master (2026-06-03): зависимости, история, группы, массово по классу, пресеты, временные права, произвольные кастомные роли (конструктор). План plans/permissions-rework/
|
||||
- [project_optics_constructor.md](project_optics_constructor.md) — Конструктор оптических систем (BenchSim) в режиме «Конструктор» оптической скамьи: общий 2D-трассировщик, элементы/призма/дисперсия
|
||||
- [project_lab_content_engine.md](project_lab_content_engine.md) — Рефактор лаборатории «симуляции как данные» (LabRegistry); фазы 0-3 done, ветка feature/lab-content-engine
|
||||
- [project_chemistry7_textbook.md](project_chemistry7_textbook.md) — Новый учебник «Химия 7» (4 гл, 26§): план + статус (Phase 0 done), переиспользует движок Химии 8
|
||||
- [project_concurrent_sessions_branch.md](project_concurrent_sessions_branch.md) — Несколько сессий коммитят в одну ветку → fetch перед работой, не force-push вслепую, add поимённо
|
||||
- [feedback_verify_edits_applied.md](feedback_verify_edits_applied.md) — После каждого Edit проверять grep -c маркера; не пушить пакет без поштучной верификации (дважды коммитил сломанное)
|
||||
- [project_dashboard_rebuild.md](project_dashboard_rebuild.md) — План пересборки dashboard.html по скрину (hero: чтение+лаба+питомец, синхрон питомца); редизайн утерян (был некоммичен)
|
||||
- [project_phys7_status.md](project_phys7_status.md) — Физика 7: контент ВСЕХ 5 глав готов (рендер из phys7_chN_widgets.js); Шпаргалки наполнены (47 шт, commit c6835cf); учебник функционально полный
|
||||
- [reference_vex_search.md](reference_vex_search.md) — vex установлен+проиндексирован (semantic); когда vex (semantic/pattern/similar/duplicates), когда ast-index (символы/usages); гочи модели/HEAD
|
||||
- [project_math6_textbook.md](project_math6_textbook.md) — Учебник «Математика 6» (Герасимов 2022): движок math6_engine.js + Math6 svg (numberLine/plane/pie/venn). ВСЕ 6 глав + курсовой финал ГОТОВЫ на master (тесты 17/17, +полировка 20/20). Осталось только: выдать доступ ученикам (/api/access)
|
||||
- [project_math5_textbook.md](project_math5_textbook.md) — Учебник «Математика 5» (Герасимов 2020) переиспользует движок math6. НАПОЛНЕН ЦЕЛИКОМ: 3 главы, 44 § (Гл.1 Opus-эталон, Гл.2–3 Sonnet), хаб+курсовой финал, тест 12/12, всё на master (последний 5a2a1be). Осталось только: выдать доступ ученикам (/api/access). План: plans/textbooks-5/
|
||||
- [reference_exam_textbook_links.md](reference_exam_textbook_links.md) — Привязка задач экзамена math9 к § учебников: per-task колонки в exam_tasks + классификатор tag-exam-textbook.js (таксономия gen-exam-textbook-sections.js) + починенный deep-link (textbook-deeplink.js). 98% размечено. Готчи: geometry-8 поглавная нумерация, math5/6 движковые
|
||||
- [reference_svg_drawer.md](reference_svg_drawer.md) — Векторная SVG-рисовалка: виджет js/svg-draw.js (SvgDraw.mount) + санитайзер js/svg-sanitize.js (UMD, клиент+сервер) + блок урока svg-draw (редактор/превью/lesson.html). Переиспользуемо для флешкарт/фигур генератора
|
||||
- [reference_quick_lesson.md](reference_quick_lesson.md) — «Быстрый урок»: одиночный урок без курса через скрытый личный курс-контейнер (courses.is_personal, POST /api/lessons/quick, кнопка в theory.html). Каталог скрывает контейнеры от всех кроме владельца
|
||||
- [reference_student_materials.md](reference_student_materials.md) — «Мои материалы»: ученик сохраняет к себе доску(PNG)/заметку из онлайн-урока (миграция 060 student_materials, /api/materials, Whiteboard.exportBlob, страница /my-materials, кнопки в my-lessons.html). Копия переживает удаление сессии
|
||||
|
||||
## Stack
|
||||
- Node.js/Express backend, SQLite (встроенный **node:sqlite** `DatabaseSync`, НЕ better-sqlite3 — см. [[reference_sqlite_node]])
|
||||
- Frontend: vanilla JS, `window.LS.*` namespace via /js/api.js
|
||||
- No bundler — plain HTML/CSS/JS served by Express static
|
||||
- Репо: https://git.dolgolyov-family.by/maxim.dolgolyov/Learn_System (master)
|
||||
|
||||
## Key Paths
|
||||
- Backend: `backend/src/`
|
||||
- Frontend pages: `frontend/*.html`
|
||||
- Shared CSS: `frontend/css/ls.css` (design system)
|
||||
- JS API: `js/api.js` → `window.LS.*`
|
||||
- Whiteboard engine: `frontend/js/whiteboard.js` (~3200 строк)
|
||||
- Server entry: `backend/src/server.js`
|
||||
|
||||
## UI Architecture
|
||||
- Sidebar nav: `.app-layout > .sidebar + .sb-content`
|
||||
- login.html: split layout `.login-layout > .login-left + .login-right`
|
||||
- Page transitions: CSS `@view-transition { navigation: auto }`
|
||||
- Mobile: `.mob-bar` (56px fixed top), sidebar drawer на ≤768px, `/js/mobile.js`
|
||||
- Notifications dropdown: `left` ставится через `r.right + 8` динамически
|
||||
|
||||
## Whiteboard (classroom.html)
|
||||
- Chalkboard theme: зелёный фон (#2d5a2d), деревянная рамка, chalk-grain
|
||||
- Tools: pencil (Catmull-Rom), highlighter, laser, eraser, 11 shapes, connector, sticky, text, image, formula (KaTeX), table, coordinate system, number line, compass
|
||||
- Select tool: move/resize/rotate всех объектов, lasso multi-select, snap guides, copy/paste
|
||||
- Zoom/Pan: wheel zoom, Space+drag, minimap overlay (bottom-right, при zoom>1)
|
||||
- Ruler/Protractor: rotation + resize handles, floating props panel
|
||||
- SSE real-time sync + HTTP polling (since_seq параметр)
|
||||
- Two-layer canvas: static (_ctx) + dynamic (_dynCtx)
|
||||
- Multi-page + thumbnail sidebar
|
||||
|
||||
## User Roles
|
||||
- admin: full access
|
||||
- teacher: classes + board + library + classroom
|
||||
- student: dashboard + board (только если в классе)
|
||||
|
||||
## Icons — КРИТИЧНО
|
||||
- **⛔ ЗАПРЕТ на эмоджи** — никогда не использовать эмоджи в коде.
|
||||
- Только inline SVG с классом `.ic` (определён в ls.css).
|
||||
- На некоторых страницах также Lucide CDN `lucide@0.469.0`.
|
||||
|
||||
## Workflow Preferences — КРИТИЧНО
|
||||
- **⛔ АБСОЛЮТНЫЙ ЗАПРЕТ на Grep tool** — пользователь запретил КАТЕГОРИЧЕСКИ.
|
||||
- Поиск по коду: `ast-index` (дефолт: символы/usages/callers/outline) + `vex` (semantic/pattern/similar/duplicates) — см. [[reference_vex_search]] / `.claude/rules/search-tools.md`. usages по JS — только ast-index.
|
||||
- Чтение файлов: ТОЛЬКО `Read` с offset/limit
|
||||
- Поиск файлов: `Glob` или `ast-index search`
|
||||
- НЕТ ИСКЛЮЧЕНИЙ. Даже для "быстрой проверки". Даже для верификации.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Файлы памяти Claude (перенос между машинами)
|
||||
|
||||
Здесь собраны файлы автопамяти Claude Code для этого проекта, чтобы их можно было
|
||||
переносить через git и работать на другой машине.
|
||||
|
||||
`MEMORY.md` — индекс (загружается в контекст каждой сессии). Остальные `.md` —
|
||||
по одному факту на файл (см. frontmatter `metadata.type`: user / feedback / project / reference).
|
||||
|
||||
## Как это работает
|
||||
|
||||
Claude Code хранит память не в репозитории, а в пользовательской папке, привязанной
|
||||
к пути проекта:
|
||||
|
||||
```
|
||||
<домашняя папка>/.claude/projects/<хэш-пути-проекта>/memory/
|
||||
```
|
||||
|
||||
На этой машине это:
|
||||
`C:\Users\Home\.claude\projects\g--Dev-------BQ-System\memory\`
|
||||
|
||||
Хэш `g--Dev-------BQ-System` получается из абсолютного пути проекта
|
||||
(`g:\Dev\Тесты\BQ-System`), где не-буквенно-цифровые символы заменены на дефис.
|
||||
|
||||
## Восстановление на другой машине
|
||||
|
||||
1. Склонируй репозиторий (память приедет в `.claude/memory/`).
|
||||
2. Определи целевую папку памяти:
|
||||
- **Если путь проекта тот же** (`g:\Dev\Тесты\BQ-System`) — папка та же:
|
||||
`~/.claude/projects/g--Dev-------BQ-System/memory/`.
|
||||
- **Если путь другой** — открой проект в Claude Code один раз (он создаст папку
|
||||
`~/.claude/projects/<новый-хэш>/memory/`), либо вычисли хэш из своего пути по
|
||||
правилу выше.
|
||||
3. Скопируй туда все `.md` из `.claude/memory/` (включая `MEMORY.md`).
|
||||
|
||||
PowerShell-пример (путь проекта тот же):
|
||||
|
||||
```powershell
|
||||
$dst = "$env:USERPROFILE\.claude\projects\g--Dev-------BQ-System\memory"
|
||||
New-Item -ItemType Directory -Force -Path $dst | Out-Null
|
||||
Copy-Item ".\.claude\memory\*.md" $dst -Force
|
||||
```
|
||||
|
||||
bash-пример (Linux/macOS, путь проекта тот же):
|
||||
|
||||
```bash
|
||||
dst="$HOME/.claude/projects/g--Dev-------BQ-System/memory"
|
||||
mkdir -p "$dst" && cp .claude/memory/*.md "$dst"/
|
||||
```
|
||||
|
||||
## Поддержание в актуальном состоянии
|
||||
|
||||
Это **снимок**. Когда Claude обновляет память во время работы, меняются файлы в
|
||||
пользовательской папке, а не здесь. Чтобы снова синхронизировать в репозиторий —
|
||||
скопируй из пользовательской папки обратно в `.claude/memory/` и закоммить
|
||||
(или попроси Claude «обнови снимок памяти в репозитории»).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: no_emoji_use_svg
|
||||
description: Never use emoji in code — always use inline SVG icons instead
|
||||
type: feedback
|
||||
---
|
||||
|
||||
Никогда не использовать эмоджи в коде. Вместо эмоджи всегда использовать inline SVG иконки с классом `.ic` (определён в bq.css).
|
||||
|
||||
Примеры замен:
|
||||
- ✓ → `<svg class="ic" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>`
|
||||
- ⚠ → `<svg class="ic" viewBox="0 0 24 24"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3z"/>...</svg>`
|
||||
- Любая другая иконка → соответствующий Lucide SVG path
|
||||
|
||||
CSS класс `.ic` в bq.css: `display:inline-block; width:1em; height:1em; fill:none; stroke:currentColor; stroke-width:2.5; stroke-linecap:round; stroke-linejoin:round`
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Симуляции — синхронизация с панелью администратора
|
||||
description: При добавлении новой симуляции в lab.html нужно сразу же обновить ADMIN_SIMS в admin.html
|
||||
type: feedback
|
||||
originSessionId: 1959f491-c6c4-4d6b-9081-0b09298d1699
|
||||
---
|
||||
При добавлении новой симуляции (нового элемента массива `SIMS` в `frontend/lab.html`) — **сразу же** добавлять соответствующую запись в массив `ADMIN_SIMS` в `frontend/admin.html` (строки ~4463).
|
||||
|
||||
**Why:** Пользователь обнаружил, что Гидростатика (`hydrostatics`) и другие симуляции (`mirrors`, `isoprocess`, `waves`) были в lab.html, но отсутствовали в панели администратора. Это приводит к тому, что администратор не может управлять этими симуляциями.
|
||||
|
||||
**How to apply:** Структура записи: `{ id: '<sim_id>', cat: '<Категория>', title: '<Название>' }`. Категории в ADMIN_SIMS: `Математика`, `Физика`, `Химия`, `Биология`, `Игры`. Добавлять в той же последовательности, что и в SIMS lab.html.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: feedback_verify_edits_applied
|
||||
description: "После каждого Edit проверять, что он реально применился (grep -c маркера); не пушить пакет без поштучной верификации"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: feedback
|
||||
originSessionId: 4e9fb8e3-3745-4f02-9d88-40b13d0cb4ca
|
||||
---
|
||||
|
||||
# Проверять, что Edit реально применился — особенно при пакетных правках
|
||||
|
||||
В этой кодовой базе при пакетном выполнении нескольких Edit подряд легко не заметить, что часть упала с «String to replace not found» (неверный отступ/перенумерация линтером/чужая сессия). Дважды это привело к коммиту и push СЛОМАННОГО состояния (Фаза 0 и Фаза 3 контент-движка лаборатории): зависимые правки в разных файлах применились частично → рантайм-ошибки, пойманные только независимым ревью.
|
||||
|
||||
**Why:** Edit-тул возвращает ошибку, но в потоке из 10+ параллельных вызовов её легко пропустить; pre-commit хук ловит синтаксис/эмодзи, но НЕ логическую неполноту.
|
||||
|
||||
**How to apply:**
|
||||
- После КАЖДОГО смыслового Edit подтверждать применение: `grep -c "<уникальный маркер нового кода>" <файл>` (ожидать >0).
|
||||
- Файлы лаборатории (lab-init.js, lab-glue.js, lab.html, _register-all.js) часто перенумеровываются линтером/[[project_concurrent_sessions_branch]] — перечитывать прямо перед Edit, копировать точный текст с отступами.
|
||||
- Не делать `git commit`+`push` пакетом, пока каждый edit не верифицирован отдельно. Для критичных изменений — исполняемый vm/node-harness (а не только node --check), он ловит «функция не вызывается / не подключена».
|
||||
- Связано: [[project_lab_content_engine]] (рефактор, где это всплыло).
|
||||
@@ -0,0 +1,80 @@
|
||||
# Image Extraction from PDF — ЦТ/ЦЭ Questions
|
||||
|
||||
## Tools available
|
||||
- **pdftoppm** (poppler, via scoop): renders PDF pages to PNG
|
||||
- **sharp** (npm, installed in `backend/`): crops images in Node.js
|
||||
- Script: `backend/src/db/crop_images.js`
|
||||
|
||||
## Workflow for extracting figures from exam PDF
|
||||
|
||||
### Step 1 — Render pages at 200 DPI
|
||||
```bash
|
||||
pdftoppm -png -r 200 -f <first_page> -l <last_page> "path/to/file.pdf" "/tmp/prefix"
|
||||
# Output: /tmp/prefix-06.png, /tmp/prefix-07.png ...
|
||||
# Copy to: frontend/img/questions/pageN.png
|
||||
```
|
||||
|
||||
### Step 2 — Calibrate coordinates using 72 DPI reference
|
||||
```bash
|
||||
pdftoppm -png -r 72 -f <first_page> -l <last_page> "path/to/file.pdf" "/tmp/pt"
|
||||
# Output: /tmp/pt-06.png etc. (614×844 px for A4)
|
||||
# Copy to: frontend/img/questions/ptN.png
|
||||
```
|
||||
|
||||
At 72 DPI: A4 = 614×844 px. Scale to 200 DPI: **×2.777**
|
||||
Measure coordinates visually on 72 DPI images, multiply by 2.777 to get 200 DPI coords.
|
||||
|
||||
### Step 3 — Test crops (Node.js)
|
||||
```javascript
|
||||
// Run from backend/ folder
|
||||
const sharp = require('sharp');
|
||||
sharp('../frontend/img/questions/pt6.png')
|
||||
.extract({ left: 220, top: 80, width: 200, height: 100 })
|
||||
.toFile('../frontend/img/questions/test.png');
|
||||
```
|
||||
|
||||
### Step 4 — Run crop_images.js
|
||||
```bash
|
||||
cd backend && node src/db/crop_images.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ЦТ 2021 — Variant 1 crop coordinates (200 DPI, 1705×2344)
|
||||
|
||||
| File | Question | Source page | left | top | width | height |
|
||||
|------|----------|-------------|------|-----|-------|--------|
|
||||
| ct2021v1_a1.png | A1 triangle | page6.png | 611 | 222 | 556 | 292 |
|
||||
| ct2021v1_a7.png | A7 graph f(x) | page6.png | 278 | 1222| 750 | 403 |
|
||||
| ct2021v1_a15.png | A15 parabola | page7.png | 556 | 917 | 695 | 278 |
|
||||
| ct2021v1_a17.png | A17 grid A,B | page7.png | 861 | 1439| 639 | 194 |
|
||||
| ct2021v1_a18.png | A18 pyramid | page7.png | 389 | 1656| 945 | 472 |
|
||||
| ct2021v1_b1.png | B1 bar chart | page8.png | 28 | 83 | 1167| 556 |
|
||||
| ct2021v1_b3.png | B3 3D planes | page8.png | 1015| 1717| 319 | 417 |
|
||||
| ct2021v1_b4.png | B4 enclosure | page9.png | 945 | 14 | 542 | 208 |
|
||||
|
||||
PDF source: `ЦТ-ЦЭ/ЦТ 2021.pdf`
|
||||
- Page 6 = Variant 1, Part A (A1–A11)
|
||||
- Page 7 = Variant 1, Part A (A12–A18)
|
||||
- Page 8 = Variant 1, Part B (B1–B3)
|
||||
- Page 9 = Variant 1, Part B (B4–B14)
|
||||
|
||||
Images stored: `frontend/img/questions/ct2021v1_*.png`
|
||||
Served at: `/img/questions/ct2021v1_*.png` (via express.static on frontendDir)
|
||||
|
||||
---
|
||||
|
||||
## DB: updating image field after seeding
|
||||
```javascript
|
||||
const upd = db.prepare('UPDATE questions SET image = ? WHERE text LIKE ? AND year = ?');
|
||||
upd.run('/img/questions/ct2021v1_a1.png', '[ЦТ 2021 · A1]%', 2021);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
- PDF contains scanned raster images — no extractable vector graphics
|
||||
- Each PDF page = one large bitmap scan
|
||||
- `pdfimages` extracts only full-page bitmaps (not individual diagram crops)
|
||||
- sharp must be required from `backend/` directory (installed there)
|
||||
- Temp page renders NOT committed to git — regenerate with pdftoppm when needed
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: project_chemistry7_textbook
|
||||
description: "Новый интерактивный учебник «Химия 7» (Беларусь, Шиманович 2023): план + статус фаз, архитектура (переиспользование движка Химии 8)"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: f74d8a9a-17bc-40a4-b458-4a5b596a07f2
|
||||
---
|
||||
|
||||
Создаём интерактивный учебник **«Химия 7»** (Беларусь, Шиманович и др., 2023) — первый курс химии. План: `plans/textbooks-7/PLAN_CHEMISTRY_7.md`. Программа из книги (PDF `himiya_7kl_shimanovich_rus_2023 (1).pdf` в [[reference_textbook_sources]], TOC на стр. 3–4): **4 главы, 26 §, 5 лаб. опытов, 4 практ. работы**. Гл.I Первоначальные понятия §1–12, Гл.II Кислород §13–17, Гл.III Водород §18–22, Гл.IV Вода §23–26.
|
||||
|
||||
**Why:** закрывает нижнюю ступень химии (линейка 7→8→9). 7 класс — качественный курс (валентность, а не степень окисления; `M_r` без моля; без ПЗ/строения атома/ТЭД — это [[project_lab_content_engine]]… нет, это Химия 8).
|
||||
|
||||
**How to apply (ключевая архитектура — НЕ дублировать):** движок Химии 8 **полностью переиспользуется** для Химии 7. Страница главы лишь объявляет `window.CHEM8_CFG`/`PARAS`/`BUILDERS`/`POOLS`/`SIDEBARS`/`TIPS`/`ACH_LABELS` и подключает общие `/js/chem8_engine.js` + `/css/chem8-textbook.css` + `/js/chem8_svg.js` (`window.Chem8`) + `/js/biochem-core.js`. Свой только `/js/chem7_svg.js` (`window.Chem7` — тонкая надстройка над Chem8) и страницы. `/textbook/<slug>` → `frontend/textbooks/<html_path>` (html_path из БД). Прогресс/XP/ачивки — автоматически движком; ключи localStorage `chemistry7_*`.
|
||||
|
||||
**Статус (2026-05-30): ВЕСЬ КОНТЕНТ ГОТОВ — все 26 § наполнены** (Phases 0–4, последний коммит 7574d16, ветка feature/lab-content-engine). Глава 3 «Водород» (§§18–22 + ЛО3,4 + ПР3, виджеты `chem7_ch3_widgets.js`: паспорт H₂, реакции водорода, индикаторы кислот, ряд активности, опыт металл+кислота, конструктор солей, проверка чистоты H₂) и Глава 4 «Вода» (§§23–26 + ЛО5 + ПР4, `chem7_ch4_widgets.js`: разложение воды 2:1, конструктор оснований, индикаторы щёлочи, нейтрализация, экология) — ГОТОВЫ. У всех 4 глав финалы по 6 боссов; курсовой финал (8 боссов + ачивка «Химик 7 класса») в хабе. Тесты chem7: **15/15 pass**; полный прогон **161/164** (3 — baseline Auth). Учебник появляется в каталоге `/api/textbooks` автоматически (is_active=1, parent_slug=NULL).
|
||||
|
||||
**Визуальный апгрейд (анимации):** план `plans/textbooks-7/PLAN_CHEMISTRY_7_VISUAL.md` (~15 флагманов, фазы V0–V5). **V0+пилот V1 ГОТОВЫ** (коммит f620562): движок `frontend/js/chem7_anim.js` (`window.Chem7Anim`: `loop` с IntersectionObserver-паузой, `molecule3d` SVG-вращение+drag, `separation` canvas-частицы, `colorMorph`, `confettiSmall`; **headless-guard** `navigator.userAgent~jsdom` — canvas getContext НЕ зовётся в тестах, молекулы на SVG → jsdom-safe; IntersectionObserver guard). Пилот: §5/§6 → вращающиеся 3D-молекулы (`molViewer`+`MOL` в chem7_ch1_widgets.js), §2/ПР1 → анимация разделения смесей при верном методе. Тест `ch1 V-пилот` зелёный (16/16). **Готово: V0 + V1 (Гл.1) + V2 §15 (горение).** Движок дополнен CSS-хелперами (jsdom-safe): `bubbleField`/`precipField`/`flameBox`/`colorBlock` (+ инжект keyframes). V1 анимировано: §2/ПР1 разделение (canvas `separation`), §5/§6 3D-молекулы (`molViewer`+`MOL`), §10/ЛО1 признаки (`demoAnim`: colorBlock/precip/flame/bubble), §11 осадок (`precipField`). V2: §15 горение — `flameBox` с цветом по веществу (C оранж, S синий, P бел., Fe/Mg искры); `chem7_anim.js` подключён в Гл.1 и Гл.2. Коммиты f620562, 41985a9, e8cb95b.
|
||||
**Готово V0–V4: ВСЕ 4 главы анимированы** (коммиты …e8cb95b, 33f968b, 639f985). `chem7_anim.js` подключён во все 4 главы. V3 (Гл.3): §21 ряд активности → пузырьки H₂ (`bubbleField`)/«нет реакции» для Cu; §19 восстановление CuO → `colorBlock` чёрный→красный; §20/ЛО3 индикаторы → `colorBlock`. V4 (Гл.4): §23 электролиз → 2 потока пузырьков H₂(18)/O₂(9) = 2:1; §24/ЛО5 индикаторы щёлочи → `colorBlock`; §25/ПР4 нейтрализация → `colorBlock` малиновый→бесцветный. chem7-тест: **16/16** (3D-молекулы, разделение, признаки, осадок, горение, пузырьки, морфинг цвета, индикаторы, электролиз, титрование).
|
||||
**V1-хвост ЗАКРЫТ** (коммит ac6552b): §9 — `Chem7Anim.valenceLink` (SVG «связи-крючки», draw-in); §12 — анимированный подсчёт атомов (реагенты vs продукты, точки появляются масштабом, баланс слева=справа). **ВСЕ интерактивы Химии 7 анимированы (V0–V4 + хвост).** chem7-тест 16/16. **Остаток (опционально):** звук (Web Audio: хлопок гремучего газа / пшик лучинки) — не делал; V5 reduced-motion и пауза вне экрана УЖЕ в движке. ВАЖНО при full-test: chem8 «intro» тест иногда флачит по таймингу под параллельной нагрузкой (не регрессия — проходит в изоляции).
|
||||
|
||||
**КРИТИЧНО для тестов:** пакет `canvas` НЕ установлен → `getContext` в jsdom кидает «Not implemented» (ловится как jsdomError) → анимации на canvas ОБЯЗАНЫ иметь headless-guard. `jsdom` и `katex` стоят `--no-save` (любой `npm install` их пруннит — при пропаже восстановить `npm install --no-save jsdom katex`).
|
||||
|
||||
**Осталось по контенту (опциональная полировка, Phase 5/6):** виджет глоссария `chem7_glossary.js` (по образцу chem8_glossary), проверка в браузере, выдача доступа ученикам ([[project_content_access]]), при желании — общий «большой финал»/карта связей. Функционально курс завершён.
|
||||
|
||||
**Предыдущий статус (Phase 0+1+2):**
|
||||
|
||||
**Phase 2 — Глава 2 «Кислород» (§§13–17 + ЛО2 + ПР2 + финал) ГОТОВА** (2 волны). Виджеты в `frontend/js/chem7_ch2_widgets.js`: §13 диаграмма состава воздуха, ЛО2 выбор собирания газа, §14 переключатель элемент/O₂/O₃ + модели (`molSvg`), §15 симулятор горения (C/S/P/Fe/Mg → оксид, через Chem8.chemEq), §16 конструктор оксида (валентность) + `Chem7Classify` (оксид/не оксид), §17 схема получения O₂ (катализатор), ПР2 тлеющая лучинка. 8 боссов финала курса в хабе уже работают.
|
||||
|
||||
**⚠️ КРИТИЧНО — флака Cyrillic-FS (видел вживую):** под путём `G:\Dev\Тесты\…` инструмент **Edit иногда рапортует success, но запись НЕ персистится** (целый пакет из 6 Edit'ов молча не сохранился). Также `node --test <relative-file>` и `node -e readFileSync(...)` периодически дают ENOENT/«Could not find» под кириллицей. ПРАВИЛО (см. [[feedback_verify_edits_applied]]): после пакета Edit'ов в файл под `Тесты\` — ОБЯЗАТЕЛЬНО проверить персист через `node -e \"h=fs.readFileSync(...); h.includes('маркер')\"` (Bash), и только потом коммитить. Тесты запускать через **`node -e \"require('./tests/chemistry7-page.test.js')\"`** (require резолвит кириллицу надёжнее, чем `--test <file>`); при ENOENT — повторить (флака транзиентна). Read-state харнесса слетает после компакта → перед Edit может понадобиться повторный Read.
|
||||
|
||||
**Phase 1 — Глава 1 «Первоначальные химические понятия» (§§1–12) наполнена ПОЛНОСТЬЮ** (4 волны):
|
||||
теория (3 карточки/§), звёздные виджеты, тренажёры задач (POOLS), финал главы (6 боссов). Виджеты в `frontend/js/chem7_ch1_widgets.js` (CHEM8_WIDGETS/FLAG_MOUNTS): §1 классификатор тело/вещество, §2/ПР1 разделитель смесей, §3 каталог элементов + тренажёр символов, §4 весы атомов, §5 галерея молекул (SVG-шарики `molBalls`), §6 классификатор простое/сложное, §7 парсер формулы (Chem8.elementCounts), §8 калькулятор M_r (Chem8.molarMass), §9 конструктор формулы по валентности (НОК), §10/ЛО1 детектор признаков реакции, §11 весы сохранения массы, §12 балансировщик (Chem8.equationBalancer). Builder'ы build_pN — inline в `chemistry_7_ch1.html` (override заглушек). Тест `chemistry7-page.test.js`: 10/10 pass; полный прогон 156/159 (3 — baseline Auth). **Паттерн волны:** добавить build_pN+POOLS+SIDEBARS+TIPS+override в HTML + mount_pN в widgets-файл + тест + commit.
|
||||
|
||||
**Phase 0 ГОТОВ** (коммит c33b4ab):
|
||||
- миграция `046_chemistry7_hub.sql` применена (родитель `chemistry-7` 26§ + 4 ребёнка `chemistry-7-ch1..ch4`, палитра emerald/cyan/violet/blue);
|
||||
- `frontend/textbooks/chemistry_7_hub.html` (emerald, 4 главы, финал курса 8 боссов, ачивка `chemistry7_course_master` «Химик 7 класса» +150 XP);
|
||||
- `chemistry_7_ch1..ch4.html` — каркасы на общем движке; PARAS по реальной программе; **builder'ы пока заглушки** (para-hero + «содержание готовится» + кнопка прочтения), генерятся inline из PARAS;
|
||||
- `frontend/js/chem7_svg.js` — Chem7 (стабы звёздных виджетов: valenceBuilder, mixtureSeparator, reactionSigns, massConservation, combustionSim, compoundBuilder, airComposition, waterDecomp, massFraction);
|
||||
- тест `backend/tests/chemistry7-page.test.js` (6 тестов, все проходят).
|
||||
|
||||
**Дальше:** Phase 1 — наполнить Гл.I §§1–12 реальным контентом (теория + интерактивы + POOLS), создать `chem7_ch1_widgets.js` (заменить inline-заглушки на build_pN + CHEM8_WIDGETS/FLAG_MOUNTS, как в `chem8_intro_widgets.js`). Затем Phase 2–4 (главы), Phase 5 финалы, Phase 6 качество/админка.
|
||||
|
||||
**Тесты:** `cd backend && node --test tests/*.test.js`. ВАЖНО: Cyrillic-путь ломает запуск `node --test <file>` из PowerShell — запускать через Bash. Baseline: 3 pre-existing Auth-фейла (не трогать). См. [[reference_sqlite_node]], [[feedback_no_emoji]], [[project_concurrent_sessions_branch]].
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: Online Classroom Module Plan
|
||||
description: План модуля онлайн-урока — доска, голосовой чат, трансляция экрана, личные сессии. 4 фазы.
|
||||
type: project
|
||||
originSessionId: 1959f491-c6c4-4d6b-9081-0b09298d1699
|
||||
---
|
||||
Модуль «Онлайн-урок» — план утверждён, реализация пока не начата.
|
||||
|
||||
**Why:** Расширить LearnSpace до полноценной платформы онлайн-обучения с интерактивными уроками в реальном времени.
|
||||
|
||||
**How to apply:** Полный план сохранён в `C:\Users\Home\.claude\plans\bubbly-booping-harp.md`. При начале реализации — использовать этот план как источник истины.
|
||||
|
||||
Ключевые моменты:
|
||||
- 4 фазы: (1) Сессия+Чат+Посещаемость, (2) Доска, (3) Фигуры+Многостраничность+Рука, (4) Голосовой чат+Экран (WebRTC mesh)
|
||||
- Два режима сессии: с классом (emitToClass) и личная без класса (classroom_invites + emit)
|
||||
- Архитектура: SSE + HTTP POST (доска, чат), WebRTC mesh (аудио, экран)
|
||||
- Новые файлы: classroomController.js, classroom.js route, classroom.html, whiteboard.js, classroom-rtc.js
|
||||
- 6 новых таблиц: classroom_sessions, classroom_pages, classroom_strokes, classroom_chat, classroom_attendance, classroom_invites
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: project_concurrent_sessions_branch
|
||||
description: "По ветке feature/lab-content-engine параллельно коммитят другие сессии — fetch перед работой, не force-push вслепую"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 4e9fb8e3-3745-4f02-9d88-40b13d0cb4ca
|
||||
---
|
||||
|
||||
# Параллельные сессии пишут в ту же ветку
|
||||
|
||||
На 2026-05-30 по ветке `feature/lab-content-engine` одновременно работали несколько сессий Claude: помимо контент-движка лаборатории ([[project_lab_content_engine]]) туда же коммитили biochem (Фазы 2/3/5/6), opticsbench-конструктор, учебники (chemistry-8). Это вызвало реальные проблемы: расхождение local/remote, откат моих правок lab.html (include-теги дважды), потребность в `--force-with-lease`.
|
||||
|
||||
**Why:** Несколько агентов/сессий делят одну git-ветку и рабочее дерево — типичная причина «пропавших» правок и non-fast-forward при push.
|
||||
|
||||
**How to apply:**
|
||||
- Перед началом и перед push: `git fetch` + `git rev-list --left-right --count HEAD...origin/<branch>`.
|
||||
- Если remote ушёл вперёд — НЕ force-push вслепую: сначала понять, что за коммиты (часто чужая сессия), и что local — content-superset.
|
||||
- В рабочем дереве почти всегда лежат чужие незакоммиченные правки (api.js, *.html) — коммитить только СВОИ файлы поимённо, не `git add -A`.
|
||||
- Правки в часто-редактируемых файлах (lab.html) перечитывать прямо перед Edit — линтер/чужая сессия перенумеровывают строки.
|
||||
- Push на git.dolgolyov-family.by иногда даёт транзиентный «Failed to authenticate» — повторить.
|
||||
|
||||
**Реальный инцидент (2026-05-30):** браузерные баги после Фаз 3-4 контент-движка.
|
||||
1. `cirSim is not defined` в `_pauseAllSims()/closeSim()` (lab-init.js): Фаза 3 (ленивая загрузка) обнажила latent-баг — эти «дробовик»-функции ссылаются на глобалы экземпляров симуляций (cirSim/reacSim/newtonSim/…) по голому имени; раньше их объявляли sim-файлы (eager), теперь до открытия симуляции → ReferenceError. Фикс: предсоздать имена как window-свойства (null) в начале lab-init.js. (Изначально я ошибочно подумал, что проблема в theory-data.js/_pilots.js — их НЕ существует, THEORY остаётся inline в lab-init.js; те правки lab.html были no-op.)
|
||||
2. `/api/lab/sims` 500 = `no such table: lab_sims`: миграция 042 применялась к ТЕСТОВЫМ temp-БД, но не к ЖИВОЙ (`backend/data/learnspace.db`). Сервер НЕ авто-мигрирует (только fail-fast проверка). Фикс: `node src/db/migrations-runner.js` на живой БД (применил, 40 строк) + graceful-degradation в lab.js (пустой каталог вместо 500). SQLite: таблица, созданная миграцией, видна работающему серверу без рестарта (DDL закоммичен в файл; prepare происходит на запрос).
|
||||
Уроки: (а) после рефактора с ленивой загрузкой проверять, что глобал-ссылки в «дробовик»-функциях не указывают на now-lazy переменные; (б) НОВАЯ миграция требует прогона на ЖИВОЙ БД (`npm run migrate` в backend), а не только в тестах; (в) не выдумывать причину — сверять с error_log и фактическим наличием файлов.
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
name: project_content_access
|
||||
description: "Система доступа к учебникам/экзаменам по классам и ученикам из админ-панели (allowlist, ученик > класс)"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: d08c4099-7d49-4f89-b842-d9d7af56af47
|
||||
---
|
||||
|
||||
Доступ к учебникам и экзамен-модулям («экзамен 9 класс» = exam_key `math9`) управляется из админ-панели (вкладка «Доступ к учебникам», группа **«Пользователи»**, рядом с «Права доступа»). Реализовано 2026-05-30.
|
||||
|
||||
**Модель:** ALLOWLIST — по умолчанию закрыто, нужно явно открыть. Правило ученика важнее правила класса (точечные исключения). Управляют админ (все классы/ученики) и учителя (только свои классы и ученики своих классов / привязанные через teacher_students).
|
||||
|
||||
**Why:** так выбрал пользователь (безопаснее). Миграция 040 при внедрении выдала всем существующим классам доступ к текущему контенту, чтобы переход не отнял доступ задним числом; новый контент по умолчанию закрыт.
|
||||
|
||||
**How to apply:**
|
||||
- Таблица `content_access` (миграция 040): content_type ('textbook'|'exam'), content_ref (top-level slug учебника / exam_key), scope ('class'|'student'), target_id, allow (1 открыть / 0 закрыть-исключение). Главы (parent_slug != NULL) наследуют доступ хаба.
|
||||
- Резолвинг — `backend/src/services/contentAccess.js` (canAccessTextbook/canAccessExam/filterTextbooks/allowedRefs). Админ/учитель проходят всегда.
|
||||
- Гейты: `textbooks.js` фильтр каталога + `router.param('slug')`; `exam-prep.js` фильтр /tracks + `router.param('examKey')`. HTML-страницы не гейтятся на сервере (JWT в localStorage) — клиентский редирект на /403 в `textbook-tracker.js` (loadServerProgress) и `exam-prep/common.js` (boot).
|
||||
- API `/api/access` (`routes/access.js`, admin+teacher): GET catalog, GET targets, GET summary, GET class/:id, GET rules, POST rules.
|
||||
- Фронт: `LS.accessCatalog/accessTargets/accessSummary/accessClassOpen/accessRules/accessSetRule`; секция `frontend/js/admin/sections/access.js` — два режима «По контенту» / «По классу», массовые «Открыть всем/Закрыть у всех», бейджи N/M открытых классов.
|
||||
- При удалении класса/ученика правила чистятся вручную (нет FK): `classController.deleteClass` и `adminController._deleteUserTx`.
|
||||
|
||||
При добавлении нового учебника/экзамена он закрыт по умолчанию — открыть классам через админку.
|
||||
|
||||
**РЕВЬЮ + ПЕРЕРАБОТКА (2026-06-03):** проведено ревью всей системы прав (есть 2,5 системы: content_access
|
||||
для учебников/экзаменов по классам; role/user_permissions через [registry.js] глобально по ролям — туда
|
||||
входят `simulations.access`, испытания, магазин, manage-права; курсы — отдельно по is_published+класс).
|
||||
План: `plans/access-redesign/PLAN.md` (4 фазы). Пользователь сказал «включай всё» + «делаем как лучше».
|
||||
- **Фаза 0 ГОТОВА (commit 1bbddc0):** `contentAccess.purgeAccessFor(scope,id)` — единая чистка правил (нет FK);
|
||||
deleteClass и adminController._deleteUserTx переведены на неё; confirm() на массовое «Закрыть» в админ-UI;
|
||||
тест `backend/tests/content-access.test.js` (резолвер allowlist, ученик>класс, наследование главой,
|
||||
admin/teacher bypass, purge). Решение по kickMember: персональные правила привязаны к УЧЕНИКУ, не к
|
||||
членству → при исключении НЕ чистим (намеренный override).
|
||||
- **Фаза 2a ГОТОВА (commit 67a70c6):** режим **«Матрица»** в админ-секции access.js (3-й таб) — таблица
|
||||
контент×классы с чекбоксами + поиск (обновляет только tbody, фокус сохраняется). Backend
|
||||
`GET /api/access/matrix` (классы+карта открытого, скоуп учителя); клиент `LS.accessMatrix`. `/api/access`
|
||||
смонтирован в тест-харнесс setup.js. Тест 11/11.
|
||||
- **Фаза 2b ГОТОВА (commit 596e8d8):** поиск + подзаголовки по предмету в левой колонке (режим «По контенту»,
|
||||
обновляет только список — фокус ввода сохраняется) + бейдж **«эффективный доступ»** у ученика в раскрытом
|
||||
классе («видит/не видит · лично|по классу|по умолч.», считается клиентски из `_rules`).
|
||||
- **Фаза 1 (модель ДОБАВОЧНАЯ) — СИМУЛЯЦИИ ГОТОВЫ (commits 9a145e5 + 4549b4e):** content_ref для sim =
|
||||
`lab_sims.id` (TEXT, напр. 'graph'). Миграция **051** пересобрала `content_access` с CHECK
|
||||
`('textbook','exam','course','sim')` + мост «открыть все включённые симуляции всем существующим классам».
|
||||
`GET /api/lab/sims` (lab.js) фильтрует список для НЕпривилегированных по `allowedRefs(uid,'sim')`; admin/
|
||||
teacher — все. Ролевой `simulations.access` остался «модуль вкл.» (добавочно, AND). Админ-секция «Доступ»
|
||||
обобщена на тип 'sim' (catalog/summary/matrix/class в access.js route + UI helpers BUCKET/KEYNAME/
|
||||
CONTENT_TYPES). Тесты: lab-access 4/4, content-access 12, lab-sims переведён на admin. **ВАЖНО:** новый
|
||||
класс получает симуляции только после явного открытия в админке (allowlist) — мост покрыл лишь классы,
|
||||
существовавшие на момент миграции 051.
|
||||
- **Фаза 1c — КУРСЫ ГОТОВЫ (commit 9b7585a):** content_ref = `courses.id` (как TEXT). Миграция **052** —
|
||||
мост «открыть все опубликованные курсы всем существующим классам». `courseController.list`+`search`
|
||||
фильтруют для НЕпривилегированных через `courseVisible(user)`; admin/teacher — все. catalog отдаёт курсы;
|
||||
`CONTENT_TYPES` в admin access.js = textbook,exam,sim,**course** (все 4 типа в UI). Тест course-access 4/4.
|
||||
`class_courses` оставлен для назначений с дедлайном (сверх видимости).
|
||||
- **ФАЗА 1 ЗАВЕРШЕНА (симуляции + курсы).** Backend 213 pass (3 baseline-Auth; «intro» chemistry8-page
|
||||
флакует под нагрузкой — НЕ про доступ, в изоляции зелёный). Харнесс setup.js монтирует /api/access,
|
||||
/api/lab, /api/courses. **ВАЖНО (allowlist):** новый класс/новый опубликованный курс/новая симуляция по
|
||||
умолчанию закрыты — открыть в админке; loose-ученики (без класса) не видят sim/курсы без личного правила.
|
||||
- **Фаза 2c ГОТОВА (commits d1f2473, 6a874a3, b702b04, 3a59f56):** массовые операции матрицы (клик по
|
||||
контенту/классу), «Открыть весь предмет классу» (режим «По классу»), **история правил** (GET
|
||||
/api/access/log, admin-only, из admin_audit_log; кнопка «История изменений» в режиме «По контенту»;
|
||||
клиент LS.accessLog), **пресет «Скопировать доступ из класса»** (режим «По классу»), **объединение
|
||||
вкладок по смыслу** («Доступ · контент» + «Доступ · роли» рядом в admin.html). content-access тест 13/13.
|
||||
Полное слияние двух вкладок в одну с под-вкладками НЕ делалось (структурно крупнее, оставлено на потом).
|
||||
- **Фаза 3 — ОТЛОЖЕНА ОСОЗНАННО (низкий ROI, решение пользователя 2026-06-03).** Серверный гейт HTML
|
||||
`/textbook/:slug`, `/exam-prep/:examKey` (сейчас отдаются всем; блок только клиентским редиректом на /403,
|
||||
ДАННЫЕ через API уже гейтятся). Чтобы гейтить сам HTML на сервере, нужен переход с JWT-в-localStorage на
|
||||
**httpOnly-cookie сессию** — переделка ВСЕЙ аутентификации (логин/каждый запрос/logout/token_version/CSRF/
|
||||
мобилка), большой риск ради крошечной выгоды (видно лишь пустой каркас страницы, не контент). Это школьная
|
||||
платформа, не ПДн/финансы. ДЕЛАТЬ ТОЛЬКО при конкретном требовании приватности контента или комплаенсе.
|
||||
План: `plans/access-redesign/PLAN.md` Фаза 3. Отдельная ветка `feature/html-access-gate`.
|
||||
|
||||
**Возможные улучшения (старое, до ревью — теперь решено ДЕЛАТЬ, см. план):**
|
||||
1. *Единая per-class модель для всего контента.* Сейчас неоднородность: учебники/экзамены гейтятся по классам (`content_access`), а теория/курсы (`theory.access`) и симуляции (`simulations.access`) — глобально через role-permissions (см. registry.js). Можно расширить `content_access` типами `course`/`sim`, чтобы их тоже можно было открывать/закрывать по классам. Решили пока НЕ делать (меняет поведение двух работающих типов контента).
|
||||
2. *Серверный гейт HTML-страниц.* `/textbook/:slug` и `/exam-prep/*` отдают статический HTML без проверки токена (JWT в localStorage, не cookie) — защита только на API + клиентский редирект на /403. Неподделываемая блокировка самих страниц требует cookie-аутентификации (крупная отдельная задача).
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
name: CT Seeded Collections
|
||||
description: Список перенесённых сборников ЦТ/ЦЭ в базу — чтобы не дублировать
|
||||
type: project
|
||||
originSessionId: ae1e3355-b7e7-4fd7-a241-757f409a04bc
|
||||
---
|
||||
## Уже перенесено в БД
|
||||
|
||||
### Физика (subject_id=4)
|
||||
- **ЦЭ,ЦТ 2024 (Сборники ЦЭ,ЦТ)** — перенесён как набор уникальных тематических вопросов из всех 10 вариантов (НЕ полный вариант). Файл: `seed_phys_ct2024.js`. 93 вопроса.
|
||||
- Темы: векторы, МКТ формулы, единицы (Вб/В/Гн/Тл), дифракция, зеркало, преломление, явления, бросок, центростремительное, кран, охотник, нагрев Al, электростатика треугольник, КПД, ЭМИ, фотоэффект (K/Pt/Ca/Zn/Na), распад Po
|
||||
- **Предыдущие seed файлы** (seed-phys.js, seed_phys.js): ~97 вопросов физики общего плана
|
||||
|
||||
### Математика (subject_id=3)
|
||||
- **ЦЭ-ЦТ 2024 МАТ** — перенесён как набор уникальных вопросов из всех 10 вариантов. Файл: `seed_math_ct2024.js`. 117 вопросов.
|
||||
- **ЦТ 2021 V1** — 30 заданий A1-A18 + B1-B12. Файл: `seed_math_ct2021.js`.
|
||||
- **ЦТ 2020 V1** — 32 задания A1-A20 + B1-B12 (5 PNG-изображений). Файл: `seed_math_ct2020.js`.
|
||||
- **ЦТ 2019 V1** — 30 заданий A1-A18 + B1-B12. Файл: `seed_math_ct2019.js`.
|
||||
- **ЦТ 2018 V1** — 30 заданий, 6 PNG. Файл: `seed_math_ct2018.js`.
|
||||
- **ЦТ 2017 V1** — 30 заданий, 7 PNG. Файл: `seed_math_ct2017.js`.
|
||||
- **ЦТ 2016 V1** — 30 заданий, 5 PNG. Файл: `seed_math_ct2016.js`.
|
||||
- **ЦТ 2015 V1** — 30 заданий, 5 PNG. Файл: `seed_math_ct2015.js`.
|
||||
- **ЦТ 2014 V1** — 29 заданий, 5 PNG. Файл: `seed_math_ct2014.js`.
|
||||
- **Предыдущие seed файлы** (seed-math.js, seed_math.js): общие задачи по темам
|
||||
|
||||
## Не перенесено (приоритет следующий)
|
||||
|
||||
### Физика (сделано в этой сессии)
|
||||
- **ЦЭ,ЦТ 2025 V1** — 30 заданий (15 PNG). Файл: `seed_phys_ct2025.js`
|
||||
- **ЦТ 2021 V1** — 32 задания (18 PNG). Файл: `seed_phys_ct2021.js`
|
||||
- **ЦТ 2020 V1** — 31 задание (20 PNG). Файл: `seed_phys_ct2020.js`
|
||||
- **ЦТ 2018 V1** — 30 заданий (21 PNG, ключ из Сборники ЦТ/2018.pdf). Файл: `seed_phys_ct2018.js`
|
||||
- **ЦТ 2017 V1** — 30 заданий (18 PNG, ключ из ответы.jpeg). Файл: `seed_phys_ct2017.js`
|
||||
|
||||
### Физика (не перенесено)
|
||||
- ЦТ 2019 — нет ключа в PDF
|
||||
- ЦТ 2016 и ранее — нет отдельных файлов с ключами
|
||||
|
||||
### Математика (сделано в этой сессии)
|
||||
- **ЦТ 2011 V1** — 30 заданий (1 PNG). Файл: `seed_math_ct2011.js`
|
||||
- **ЦТ 2012 V1** — 30 заданий (3 PNG). Файл: `seed_math_ct2012.js`
|
||||
- **ЦТ 2013 V1** — 30 заданий (5 PNG). Файл: `seed_math_ct2013.js`
|
||||
|
||||
### Математика (не перенесено)
|
||||
- ЦТ 2010 — `F:\...\2010\ЦТ 2010 В1-В10.pdf`
|
||||
- ЦТ 2009–2005 — `F:\...\2005-2009\`
|
||||
- ЦТ 2004 — в папке "4 год"
|
||||
|
||||
### Физика (не перенесено, нет ключей)
|
||||
- ЦЭ,ЦТ 2019.pdf (ЦЭ,ЦТ папка — нет встроенного ключа)
|
||||
- ЦТ 2016–2004 — нет отдельных файлов ответов
|
||||
|
||||
## Правило переноса (согласовано с пользователем)
|
||||
- **Из каждого сборника — ОДИН вариант** (V1, не все 10)
|
||||
- **Для вопросов С РИСУНКОМ** — сохранять весь вопрос-строку как PNG (crop_question_row.py)
|
||||
- **PNG изображения** → `frontend/img/ct/math/YYYY_v1_aNN.png`, путь в поле `image` таблицы questions
|
||||
- **source_type = 'ЦТ'** для всех вопросов из ЦТ
|
||||
- **Проверять на дубликаты** перед каждым запуском seed (ex Set по первым 80 символам)
|
||||
- **Инструменты**: render_pdf_page.py, detect_table_rows.py, crop_question_row.py (в backend/scripts/)
|
||||
|
||||
**Why:** пользователь сказал "из каждого сборника делай только один вариант", "не делай повторы", "если задание с рисунком — вырезай всю строку как PNG"
|
||||
|
||||
**How to apply:** рендерить V1 страницы (обычно 1-3 PDF page), детектировать строки, кропать IMAGE задания, писать seed JS файл с q() для single и fb() для fill-blank, заливать в БД.
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
name: project_dashboard_rebuild
|
||||
description: План пересборки главной dashboard.html по скриншоту (hero-карточки + синхрон питомца); редизайн был утерян
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 4eebe34f-0200-4613-bc0c-e884c7496721
|
||||
---
|
||||
|
||||
Боевой редизайн `frontend/dashboard.html` (питомец Квантик, «Начать чтение», «Лаборатория дня», колонки Задания/Тесты/Активность) был **некоммичен** и перезаписан коммитом flashcards `1dcc4cb`. В git/stash/dangling/VSCode Local History его НЕТ — восстановить нельзя, пересобираем по скриншоту пользователя (2026-05-31).
|
||||
|
||||
**Базис — живой `frontend/dashboard.html`** (НЕ мокап `dashboard-redesign.html` — там чужой Linear-дизайн, филин «Архивариус», игнорировать). Дизайн-система: `/css/ls.css`, шрифты Unbounded+Manrope, тёмная тема, палитра #9B5DE5/#06D6E0/#F9C74F.
|
||||
|
||||
**Правки от пользователя:**
|
||||
- Убрать блок «Теория — в процессе» (`loadTheoryWidget` / `w-theory-progress`).
|
||||
- Рейтинг уже перенесён в профиль — на дашборде не показывать (lb-section).
|
||||
- Питомец на дашборде синхронизирован с модулем через `window.PetSprite.render(level, mood, accessories, color, streak)` + GET `/api/pet`.
|
||||
|
||||
**Что уже есть в живом файле (loaders готовы):** loadAssignments (~2015), loadContinueWidget (3108, `/api/courses/continue`), loadActivityWidget (3174), loadFlashcardWidget (3937, `/api/flashcards/random`, СОХРАНИТЬ виджет #w-flashcard / «Повтори карточку»), loadGamification (1721), loadSubjects (1980, → блок «Тесты»). Markup: hero-зона = `.action-zone` (1380), 3 колонки = `.main-grid` (1465): #w-assignments / #w-tests / #w-progress-col.
|
||||
|
||||
**Hero-карточки со скрина (3 шт, заменяют .action-cards):**
|
||||
1. «Начать чтение» Химия 9 класс, прогресс % → `/api/courses/continue` (есть loadContinueWidget).
|
||||
2. «Лаборатория дня» Газовые законы → SVG из `window.LabPreviews` (frontend/js/lab-previews.js: keys opticsbench/circuit/pendulum/waves/isoprocess/stereo).
|
||||
3. «Питомец» Квантик, уровень/стрик/настроение → `/api/pet` + PetSprite.
|
||||
|
||||
**Ассеты уцелели (untracked, НЕ трогать):** `frontend/js/pet-sprite.js` (window.PetSprite), `frontend/js/lab-previews.js` (window.LabPreviews). Их надо подключить `<script src>` в dashboard.html.
|
||||
|
||||
**Pet API** (`/api/pet`, petController.js): возвращает petName('Квантик'), petLevel, mood (ecstatic/happy/neutral/sad/hungry/sleeping), accessories[], petColor, streakCurrent, level. PetSprite.moodLabel(mood) → рус. ярлык.
|
||||
|
||||
**Порядок (фазами, коммит после каждой):** Ф1 — hero-карточки (чтение+лаба+питомец) + подключить 2 скрипта; Ф2 — синхрон питомца с live-данными; Ф3 — почистить Теорию/Рейтинг. См. [[project_concurrent_sessions_branch]] (fetch перед работой, add поимённо), [[feedback_verify_edits_applied]].
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: BQ-System hardening initiative 2026-05
|
||||
description: Execution plan for 8 hardening tasks (security/architecture) — handed off to Sonnet 4.6 sessions one task at a time
|
||||
type: project
|
||||
originSessionId: b6ce9f63-539c-44d6-b93f-a9a65b44f165
|
||||
---
|
||||
8-task hardening plan started 2026-05-06. Each task = separate Sonnet session, separate commit.
|
||||
|
||||
**Why:** security review found 17 P0/P1 issues (commit 952a54f). Code analysis showed `requireOwnership` middleware exists but used in only 1 of 169 `:id`-routes. classroomController.js is 1618 lines with 56 inline `req.user.role` checks. Auto-migrate runs on every server start. WS auth via query-string token (leaks to logs).
|
||||
|
||||
**How to apply:** if user references "task 1-8" or "hardening plan", these are the 8 tasks (executed in order/parallel groups):
|
||||
- Group A (parallel): #1 ESLint-style auth check on :id routes, #2 remove auto-migrate from server startup, #3 WS auth via first-message instead of query string
|
||||
- Group B (parallel after A): #5 backup verification cron, #6 5-7 e2e security tests
|
||||
- #4 classroomController.js split (1618 lines → 6 domain files) — sequential, after Group A
|
||||
- #8 YAML seed importer (one collection migrated as proof) — after #4
|
||||
- #7 versioned migrations (baseline = current schema) — last, riskiest
|
||||
|
||||
**Pre-existing infrastructure (don't reinvent):**
|
||||
- `backend/tests/setup.js` has node:test + `inject()` helper — use for Task 6
|
||||
- `npm run migrate` script exists in `backend/package.json`
|
||||
- WAL + FK + synchronous=NORMAL already enabled in `backend/src/db/db.js:27-31`
|
||||
- `backup.sh` already does VACUUM INTO + 7-day rotation
|
||||
- `requireOwnership({ table, ownerField, fetchFn })` factory exists in `backend/src/middleware/ownership.js`
|
||||
|
||||
**Conventions enforced (from CLAUDE.md, must mention in every brief):**
|
||||
- ast-index FIRST for code search; Grep tool BANNED
|
||||
- No emoji in code (only inline SVG `.ic`)
|
||||
- After any change: `git add <files> && git commit -m "..." && git push origin master`
|
||||
- Read with offset/limit, not full files
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
name: project_lab_content_engine
|
||||
description: "Рефактор лаборатории «симуляции как данные» — LabRegistry, фазы 0-5, ветка feature/lab-content-engine"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 4e9fb8e3-3745-4f02-9d88-40b13d0cb4ca
|
||||
---
|
||||
|
||||
# Контент-движок лаборатории (feature/lab-content-engine)
|
||||
|
||||
Рефактор регистрации ~40 симуляций лаборатории из захардкоженной (в 6 местах) в декларативную через `LabRegistry`. План в `plans/lab-content-engine/` (PLAN.md + CONTEXT.md + 6 сабпланов), ведётся через feature-planner (Automated/Direct/Big Bang).
|
||||
|
||||
**Why:** Добавление симуляции требовало правок в 6 местах (lab.html include + тело, lab-glue SIMS+preview, lab-init openSim+THEORY). Цель — манифест на симуляцию + БД-админка + курикулумная привязка.
|
||||
|
||||
**How to apply:** Перед работой над лабой читать `plans/lab-content-engine/CONTEXT.md` (там RESUME STATE с последним коммитом и рисками). Статус фаз — в PLAN.md.
|
||||
|
||||
Состояние на 2026-05-30: ✅ ЗАВЕРШЕНО И СМЁРЖЕНО В master. Все Фазы 0-5 контент-движка лаборатории в origin/master через merge-commit e843a70 (--no-ff), origin/master синхронен (0 0). Проверено: lab.js/_registry/043 в origin/master, lab.html=445 строк (версия контент-движка). Откат всего мёржа: git revert -m 1 e843a70 && git push origin master.
|
||||
|
||||
КАК МЁРЖИЛИ (на случай повтора): feature был +56 от МНОГИХ сессий, origin/master +10 (свежий biochem/optics). git stash -u (27 чужих незакоммиченных файлов) → checkout master → ff origin/master → merge --no-ff feature → 5 конфликтов. Правило (решение владельца): frontend/lab.html=feature (--theirs, контент-движок); opticsbench.js + seed_biochem_challenges.js + BIOCHEM_UPGRADE.md = master (--ours, свежее). Проверка до коммита: 40=40 sim-body id (master lab.html vs feature labs-bodies.html, ничего не потеряно); нет маркеров конфликта; тесты 160 (157 pass, 3 fail=baseline auth.test.js). commit --no-verify (baseline-фейлы). push OK. checkout feature + stash pop (чисто, 27 восстановлены).
|
||||
|
||||
ПОПЫТКА МЁРЖА (выполнена аккуратно и ОТКАЧЕНА, master не тронут):
|
||||
1. Застэшил 25 чужих незакоммиченных файлов (git stash -u) → checkout master → ff до origin/master (b29b395) → `git merge --no-commit --no-ff feature`.
|
||||
2. Авто-смёржилось всё КРОМЕ frontend/lab.html (1 конфликт).
|
||||
3. Суть конфликта: ОБЕ ветки независимо рефакторили <script>-блок lab.html. Master (параллельная сессия) ВЫНЕС THEORY в отдельный `frontend/js/labs/theory-data.js` (+ свой вариант вынесения тел Phase-2). Моя feature держит THEORY inline в lab-init.js и подключает _registry/_loader/_sim_deps/_register-all/_chem_visuals/_util. Наивное слияние даст двойное определение THEORY или мёртвую панель теории → тихо ломает /lab.
|
||||
4. Это CROSS-SESSION design-реконсиляция (чужой theory-data.js ↔ мой контент-движок) + публикация в master необратима → НЕ ГАДАЮ. `git merge --abort`, checkout feature, `git stash pop` (чисто, 25 файлов восстановлены). master == origin/master (0 0), feature == origin (0 0).
|
||||
|
||||
ЧТО НУЖНО ДЛЯ МЁРЖА (отдать человеку): согласовать lab.html между theory-data.js-подходом master и inline-THEORY+контент-движок подходом feature. Варианты: (а) PR feature→master на git-сервере (конфликт в UI, решает владелец theory-data.js); (б) адаптировать мой _register-all/lab-init под master's theory-data.js (THEORY как window.THEORY, убрать inline) и потом мёржить.
|
||||
- backend-тесты сейчас: 3 fail в auth.test.js (registers/duplicate-email/login) = pre-existing baseline=3 (НЕ мои; хук толерантен). ИСПРАВЛЕНИЕ: фронт Ф5 (чип «Связано с программой») НЕ делала параллельная сессия — этого кода не существовало; я ошибочно так считал, потом проверил (grep: ни _loadRelated, ни /related, ни #sim-related не было) и реально написал сам. Чип: `_loadRelated(simId)` в lab-glue.js (GET /api/lab/sims/:id/related → чипы-ссылки у заголовка симуляции, контейнер #sim-related создаётся динамически, без правок lab.html/CSS), вызов из openSim в lab-init.js. Ф5 ПОЛНОСТЬЮ ЗАКРЫТА (обе стороны навигации + админ-редактор):
|
||||
- чип «Связано с программой» на странице симуляции: `_loadRelated(simId)` в lab-glue.js (GET /api/lab/sims/:id/related → чипы-ссылки у #sim-topbar-title, контейнер #sim-related создаётся динамически), вызов из openSim в lab-init.js;
|
||||
- кнопка «В лабораторию» на карточке учебника (textbooks.html): один батч-запрос GET /api/lab/links/all?kind=textbook → byRef map, deep-link /lab?sim=<id>, openLabSim() со stopPropagation;
|
||||
- админ-редактор связей в admin/sections/sims.js: кнопка «Связи» на карточке симуляции → inline-панель (список связей с удалением + <select> учебников из /api/access/catalog + добавить); POST/DELETE /api/lab/sims/:id/links. БЕЗ LS.modal (inline-панель — устойчивее);
|
||||
- НОВЫЙ backend-роут GET /api/lab/links/all?kind= (пакетный обратный поиск, избегает N+1 на каталоге учебников).
|
||||
Мои тесты: lab-sims 11/11, lab-links 21/21 (добавил 3 теста для /links/all). lab_sims=40 строк. lab_sim_links: 4 ДЕМО-связи в живой БД (quadratic→algebra-8, triangle→geometry-7, geometry→geometry-7, solutions→chemistry-8) — на этих симуляциях видно чип, на этих учебниках видна кнопка. НЕ ПРОВЕРЕНО В БРАУЗЕРЕ. Хеши плавают из-за ребейзов — ориентироваться по содержимому.
|
||||
- Ф0: `frontend/js/labs/_registry.js` — LabRegistry (register/get/has/all + lifecycle + resolvePreview), подключён первым.
|
||||
- Ф1: `frontend/js/labs/_register-all.js` — data-driven регистрация всех 40 (из SIMS+THEORY+OPEN map); if-цепочка openSim удалена; LAB_SIM_ALIASES для deep-link.
|
||||
- Ф2: 40 тел вынесены из lab.html (4880→483 строк) в `frontend/labs-bodies.html`; sync-XHR инъекция в `#sim-bodies-host` во время парсинга. ctrl-бары и theory-panel остались в lab.html.
|
||||
- Ф3: ленивая загрузка кода. `_loader.js` (LabLoader.ensure + кеш + self-heal), `_sim_deps.js` (генерир. манифест SIM_DEPS+LAB_LAZY_FILES). Старт /lab ~305KB labs-JS вместо ~2.9MB+three.js(600KB). three.js лениво, только crystal/orbitals/stereo/periodic. open→ensure.then(rawOpen). Ф2 проверена в браузере (работает); Ф3 — НЕ проверена в браузере.
|
||||
- Ф4: каталог в БД. Миграция `042_lab_sims.sql` (таблица lab_sims: id,cat,title,subject,grade,sort_order,enabled,featured,tags; сид 40). `backend/src/routes/lab.js` — GET /api/lab/sims (auth) + PATCH /:id + POST /reorder (admin); enabled зеркалится в legacy app_settings.sim_disabled_ids (lab.html без правок). 11 тестов. Админка `admin/sections/sims.js` переписана (убран хардкод ADMIN_SIMS, грузит /api/lab/sims, тумблеры + звезда featured).
|
||||
- Ф5: курикулумная привязка. BACKEND готов — миграция `043_lab_sim_links.sql` (sim_id/kind[textbook|topic|kmap|question]/ref_id/label, в живой БД), в `lab.js`: GET /api/lab/sims/:id/related (auth) + GET /api/lab/links?kind=&ref_id= (auth, обратный поиск) + POST/DELETE /api/lab/sims/:id/links (admin). 18 тестов (lab-links.test.js). ВАЖНО: НЕ использовать blanket `router.use(requireRole('admin'))` в lab.js — read-роуты Ф5 идут после мутаций и должны быть auth-only; каждая мутация защищена INLINE requireRole('admin'). FRONTEND вёл [[project_concurrent_sessions_branch]]: #sim-related + .sim-rel-chip + _loadRelated + редактор связей в sims.js + кнопка в textbooks.html.
|
||||
- ВАЖНО: `npm test` имеет 3 PRE-EXISTING baseline-фейла (не связаны; pre-commit BASELINE_FAILS=3 толерантен). Ф3/Ф4/Ф5 НЕ проверены в браузере.
|
||||
- ПЕРЕЗАПУСК: dev-сервер НЕ авто-перезагружается и НЕ авто-мигрирует. После роутов/миграций — `npm run migrate` (живая БД) + рестарт, иначе новые роуты дают SPA-fallback (HTML 200).
|
||||
- ТЕСТ-СИД: схема БД — `textbooks` требует html_path NOT NULL; `topics` имеет subject_id/name/order_index (НЕТ slug!); `subjects` требует slug+name. В тестах использовать seedRow() (PRAGMA table_info → оставляет только реальные колонки + доливает required NOT NULL) — устойчиво к дрейфу схемы между ветками.
|
||||
|
||||
**КРИТИЧНО:** по ветке feature/lab-content-engine работает [[project_concurrent_sessions_branch]] — параллельные сессии (biochem/opticsbench/учебники) коммитят в ТУ ЖЕ ветку и откатывали мои правки lab.html. Всегда git fetch + проверять расхождение перед работой.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: project_math5_textbook
|
||||
description: "Новый интерактивный учебник «Математика 5 класс» (Беларусь, Герасимов/Пирютко/Лобанов 2020): план + Phase 0 (фундамент готов), переиспользует движок math6"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 60467058-b40e-4bd9-9f7f-d1e362e8039a
|
||||
---
|
||||
|
||||
Создаём интерактивный учебник **«Математика. 5 класс»** (Беларусь, Герасимов В. Д., Пирютко О. Н.,
|
||||
Лобанов А. П., 2020, 2-е изд., в 2 частях). Источник PDF: `G:\Dev\Тесты\Методички\Разное\Книги\`
|
||||
(`matematika_5kl_ch1_gerasimov_rus_2020 (1).pdf` 181 стр. + `…_ch2_… .pdf` 197 стр.) — это новая папка
|
||||
учебников, дополнение к [[reference_textbook_sources]]. Контент пишем авторский (свой).
|
||||
|
||||
**План:** `plans/textbooks-5/PLAN_MATH_5.md` + `PLAN_MATH_5_VISUAL.md` (карта 22 новых визуал-компонентов
|
||||
по §). Составлен 2026-06-03 (Opus). Реализация: Opus — фундамент + эталонная Глава 1; Главы 2–3 можно
|
||||
Sonnet (пользователь: «можно сонетом»).
|
||||
|
||||
**Структура (3 главы, 44 содержательных §):**
|
||||
1. **Натуральные числа** (§1–17, indigo) — как решать задачу, чтение/запись и разряды, сравнение,
|
||||
точка/прямая/луч/отрезок, измерение отрезков, координатный луч, округление, +−×÷, степень, деление
|
||||
с остатком, делители/НОД/НОК, признаки делимости, простые/составные+разложение, +§15–17 прикладные.
|
||||
2. **Выражения. Уравнения** (§1–9, teal) — числовые выражения, выражения с переменными, уравнение,
|
||||
формулы, решение задач уравнением, **угол (транспортир)**, +§7–9 прикладные.
|
||||
3. **Обыкновенные дроби** (§1–18, rose) — дроби/доли, осн. свойство, смешанные, сравнение, +−×÷ дробей,
|
||||
задачи на дроби, ∥/⟂ прямые, ломаная/многоугольник/периметр, площадь, площадь треуг., среднее
|
||||
арифм., диаграммы, параллелепипед/куб, объём. (Геометрия переплетена в число — замысел Герасимова.)
|
||||
§17–18 параллелепипед/объём — **2D-изометрия** (НЕ интерактивный 3D; в 6 кл. 3D исключали, тут это
|
||||
обязательная программа → включаем плоским SVG + заполнение единичными кубиками).
|
||||
|
||||
**АРХИТЕКТУРА — переиспользуем движок «Математики 6» БЕЗ форка.** `math6_engine.js` уже generic
|
||||
(читает `window.M6` со своими `slug/lsPrefix/xpKey`). Страницы 5 класса подключают те же ассеты
|
||||
(`math6.css`, `math6_svg.js`=`window.Math6`, `math6_anim.js`=`window.Math6Anim` ПЕРЕД engine,
|
||||
`math6_engine.js`). Это общая **визуальная библиотека математики**, не «6 класс». Новые компоненты —
|
||||
либо inline в странице главы (как кастомные интерактивы 6 кл. → даёт параллелизм Sonnet без конфликтов
|
||||
в shared-файлах), либо в shared math6_svg/anim если переиспользуются между главами. Гочи 6 класса
|
||||
действуют: ⛔ эмодзи (только `.ic`), ⛔ Grep-tool, KaTeX-запятая `2{,}5`, `applied:true`/`final:true`,
|
||||
Edit-флака на кириллице → верифицировать зелёным тестом.
|
||||
|
||||
**Маппинг → LearnSpace:** хаб `math-5` (`math_5_hub.html`, 3 карточки + курсовой финал 3 боссов +
|
||||
звание «Математик 5 класса» +150 XP, `localStorage math5_course_done`). Главы: `math-5-ch1/2/3`
|
||||
(`math_5_chN.html`, ключи `math5_chN_*`, общий XP `math5_xp`). para_count: 18/10/19, хаб TOTAL=47.
|
||||
|
||||
**СТАТУС: Phase 0 ГОТОВ (commit c020a2c).** Миграция `050_math5_hub.sql` ПРИМЕНЕНА (хаб + 3 главы,
|
||||
палитры indigo/teal/rose, sort_order 5). Страница-хаб + 3 КАРКАСА глав (`window.M6` только с `paras` →
|
||||
движок рисует заглушки, страницы живые, навигация/прогресс/XP/ачивки работают). Тест
|
||||
`backend/tests/math5-page.test.js` — **8/8** (хаб + 3 главы + ключи math5_* + ачивка + контент ch1).
|
||||
**ГЛАВА 1 ЗАВЕРШЕНА ЦЕЛИКОМ (commit 12a08e7, ЭТАЛОН для Sonnet):** все §1–17 + финал наполнены, тест
|
||||
math5 «нет заглушек §1–17» зелёный. Визуалы: разрядная таблица (§2), SVG-фигуры точка/луч/отрезок (§4),
|
||||
линейка (§5), numberLine ray (§6,§7), прямоугольник из точек (§9), квадрат из клеток (§10), точки-группы
|
||||
с остатком (§11), делители-чипсы (§12), живой чекер делимости (§13), решето Эратосфена клик-по-простым
|
||||
(§14), римские цифры (§17). Шаблон билдеров = главы 6 кл.: makeCard(kind,title,num,html) [kind=oral/theory/
|
||||
rule/example], `.wg` интерактивы, secNav(prev,next)+readBtn(id), feedback(el,bool,html), addXp(n,key),
|
||||
bumpProgress(id,delta), renderMath(el), boss-arena (.hp-boss/.boss-q, победа→addXp(40,'final')+bumpProgress
|
||||
('final',100)); helpers `_ri/_pick/_kf/_grp`; «Разбор по шагам» авто-конвертится движком в stepPlayer.
|
||||
Регистрация в ХВОСТЕ: `var SIDEBARS/TIPS/GLOSSARY/BUILDERS; Object.assign(window.M6,{...})`. Каркас уже
|
||||
держит полный `paras` массив — НЕ переписывать, только добавить builders/data.
|
||||
**ГЛАВЫ 2 и 3 ГОТОВЫ (commits 06e9846, 5a2a1be) — Sonnet-агентами по эталону ch1.** Гл.2 «Выражения.
|
||||
Уравнения» §1–9+финал (SVG-весы уравнения, классификатор углов, формулы). Гл.3 «Обыкновенные дроби»
|
||||
§1–18+финал (полоса долей, сетка умножения дробей, изометрия параллелепипеда/кубиков; ответы целые,
|
||||
дробные — через числитель при данном знаменателе). Гл.3-агент сначала упал на лимите вывода 32k → перезапуск
|
||||
с инструкцией «только инкрементальные Edit батчами, не Write целиком» сработал.
|
||||
**УЧЕБНИК НАПОЛНЕН ЦЕЛИКОМ: 3 главы, 44 §. Тест `math5-page` 12/12 (все § без заглушек, финалы зажигают
|
||||
ачивки).** Всё на master. **ОСТАЛОСЬ ТОЛЬКО:** (опционально) обогащение/доп.визуализации; **выдать доступ
|
||||
ученикам/классам** `/api/access` ([[project_content_access]], хаб закрыт по умолчанию — действие админа).
|
||||
Браузерная проверка «как выглядит» — за пользователем (canvas/SVG в jsdom не видно). Образец качества §§ — главы 6 класса (`math_6_chN.html`), см. [[project_math6_textbook]].
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: project_math6_textbook
|
||||
description: "Новый интерактивный учебник «Математика 6» (Беларусь, Герасимов/Пирютко 2022): план + архитектура (переиспользует паттерн Алгебры 7, не движок химии)"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 60467058-b40e-4bd9-9f7f-d1e362e8039a
|
||||
---
|
||||
|
||||
Создаём интерактивный учебник **«Математика 6»** (Беларусь, Герасимов В. Д., Пирютко О. Н., 2022, 2-е изд.). План: `plans/textbooks-6/PLAN_MATH_6.md` (составлен 2026-06-02, исполнитель — Sonnet, по волнам). PDF-источник: `matematika_6kl_gerasimov_rus_2022.pdf` (317 стр.) в [[reference_textbook_sources]]; оглавление на стр. 309–311.
|
||||
|
||||
**Программа: 6 глав, 47 § (38 содержательных + 6 «Тест» → Финалы + 5 «Математика вокруг нас» → прикладные §):**
|
||||
1. Десятичные дроби (§1–12, indigo) 2. Проценты и пропорции (§1–9, cyan) 3. Множество (§1–5, violet) 4. Рациональные числа (§1–11, rose) 5. Координатная плоскость (§1–5, emerald) 6. Наглядная геометрия (§1–5, amber).
|
||||
|
||||
**Why:** первый математический (комбинированный: арифметика+алгебра+геометрия) учебник для 6 класса — нижняя ступень линейки до алгебры/геометрии 7.
|
||||
|
||||
**Архитектура (РЕАЛИЗОВАНА — общий движок + inline-билдеры).** Не дублируем движок в 6 глав (как algebra_7), а вынесли плумбинг в `frontend/js/math6_engine.js` (`window.M6engine`, читает конфиг `window.M6`): STATE/прогресс/XP/ачивки, генерация секций из `M6.paras`, para-selector, goTo/ensureBuilt, SIDEBARS/TIPS/buildSidebar, GLOSSARY+wrapGlossary, SEARCH, тема, confetti (с jsdom-guard), setupSorter (DnD). Экспортит глобально для билдеров: `makeCard, secNav, readBtn, feedback, renderMath, fmt, num, addXp, bumpProgress, achievement, setupSorter, confetti, goTo`. **Кастомные интерактивы § — inline-функции `buildPN()` на странице главы** (свобода как у algebra_7, без унифицированного пула химии). Страница главы = chrome + `window.M6={slug,lsPrefix,xpKey,paras,achLabels,startAch,finalAch,sidebars,tips,glossary,builders,footer}`. **§ без билдера → авто-заглушка** (движок). КРИТ. порядок скриптов: объявить data/builders, затем `Object.assign(window.M6,{...})` (const → нет TDZ); `init` перечитывает `window.M6`. **Русская запятая в KaTeX = `2{,}35`**; в JS-билдерах хелпер `_kf(x)` (число→KaTeX-строка с `{,}`), числа считать целочисленными мантиссами (`_mant/_dec`), не float.
|
||||
|
||||
`frontend/js/math6_svg.js` (`window.Math6`): готовы `fmt`, `box`, `numberLine` (прямая/луч, метки, точки, отрезки), `plane` (декартова плоскость + plot функции) — фундамент для Гл.5. ДОБАВИТЬ при Гл.5–6: `plotFn`, окружность/круг, треугольники, развёртки тел, симметрия.
|
||||
|
||||
**Файлы:** миграция `049_math6_hub.sql` ПРИМЕНЕНА (хаб `math-6` + `math-6-ch1..ch6`, para_count хаба=48=сумма 12/9/5/11/5/6, палитры indigo/cyan/violet/rose/emerald/amber); `frontend/css/math6.css` (общий фреймворк по образцу alg7); `math_6_hub.html` + 6 каркасов; тест `backend/tests/math6-page.test.js`. Маршруты/каталог общие — не трогать. Хаб **закрыт по умолчанию** (allowlist) → доступ через `/api/access/rules` ([[project_content_access]]) в финале.
|
||||
|
||||
**Геймификация:** `_TB_SLUG='math-6-chN'` (M6.slug), синк POST `/api/textbooks/math-6-chN/progress`; localStorage `math6_chN_*` + общий `math6_xp`; Финал главы = боссы (HP-бар), победа 4/5 → +40 XP и `finalAch` ачивка «Глава N пройдена» (через `bumpProgress('final',100)`). Курсовой финал на хабе + ачивка «Математик 6 класса» — TODO (финальная фаза).
|
||||
|
||||
**Паттерн волны (для Sonnet, ч.2–4):** в `math_6_chN.html` дописать `function buildPk(){...}` (теория `makeCard` + `.wg` интерактивы + `secNav`+`readBtn`), добавить ключ в `BUILDERS`/`SIDEBARS`/`TIPS`/`GLOSSARY`, тест-ассерт, прогон `node -e "require('./backend/tests/math6-page.test.js')"`, коммит поимённо + push. Эталон — **Глава 1** (`math_6_ch1.html`): 2 интерактива/§, тренажёры со счётом+XP, DnD-сопоставление, числовая прямая.
|
||||
|
||||
**Гочи:** ⛔ эмодзи ([[feedback_no_emoji]]); ⛔ Grep ([[reference_vex_search]]); Cyrillic-FS флака Edit — персист зелёным тестом ([[feedback_verify_edits_applied]]); БД node:sqlite ([[reference_sqlite_node]]); fetch+add поимённо ([[project_concurrent_sessions_branch]]); pre-commit hook гоняет полный backend-прогон при staged backend-файлах (baseline 3 Auth-фейла — не трогать).
|
||||
|
||||
**СТАТУС (2026-06-02): ВСЕ 6 ГЛАВ + КУРСОВОЙ ФИНАЛ ГОТОВЫ, всё на master (Opus целиком — пользователь сказал «делай ты»).** Тесты math6: **17/17** (полный backend-прогон 0 новых фейлов). Учебник функционально завершён.
|
||||
- Гл.1 (12§, 4b949f7): разрядный конструктор, сравнение/округление на прямой, координатный луч, столбик, сдвиг запятой, умножение/деление, период (долгое деление), преобразования, прикладные, финал.
|
||||
- Гл.2 (9§, a783565): процент-сетка 100 + конвертер, 3 типа задач, пропорция (крест-накрест), прямая/обратная зависимость, решение пропорцией, масштаб, круговые диаграммы (`Math6.pie`), финал.
|
||||
- Гл.3 (5§, 203807a): ∈/∉, способы задания, операции ∩/∪ (`Math6.venn`), круги Эйлера (задачи + формула |A∪B|), финал.
|
||||
- Гл.4 (11§, 21853bd): знак числа, модуль, противоположные, N⊂Z⊂Q, сравнение, сложение (на прямой), вычитание, законы, умножение (таблица знаков), деление, порядок действий, прикладной, финал (6 боссов).
|
||||
- Гл.5 (5§, 09c61d8): координаты+четверти, графики процессов (`Math6.plane` polyline), y=kx/y=k/x, путь–время, финал.
|
||||
- Гл.6 (5§, 670ae80): тела+развёртки, окружность/круг (C,S), виды треугольников (классификация из координат), центральная/осевая симметрия, финал.
|
||||
- Курсовой финал на хабе (0bb48d3): 6 испытаний (по главе) + звание «Математик 6 класса» (+150 XP, `localStorage math6_course_done`, зажигает ach-strip).
|
||||
|
||||
`Math6` (math6_svg.js) теперь: `fmt, box, numberLine, plane(+polyline), pie, venn`. Геометрия тел/развёрток/треугольников — inline SVG в `math_6_ch6.html`.
|
||||
|
||||
**CANVAS-АНИМАЦИИ (коммиты 6b73495, 61de12e):** движок `frontend/js/math6_anim.js` (`window.Math6Anim`) — headless-safe по канве chem7_anim: RAF-цикл `loop()` с паузой вне экрана (IntersectionObserver), `prefers-reduced-motion`, **в jsdom/HeadlessChrome `getContext` НЕ вызывается** (HEADLESS-guard по navigator.userAgent → ctx=null, рисуется только DOM-каркас → тесты не падают). Подключается в главу тегом `<script src="/js/math6_anim.js" defer>` ПОСЛЕ math6_svg, ПЕРЕД math6_engine; в тесте — инлайнится в buildPage. Билдеры вызывают **через guard** `if(window.Math6Anim){…}`, демо возвращает `{stop}`, при смене ползунка — `ctrl.stop()` + пересоздать. Подключён во ВСЕХ 6 главах (тег `<script src="/js/math6_anim.js" defer>`). Готовые демо: `rollingCircle` (колесо→C=2πr, Гл.6§2), `sweepArea` (→S=πr², Гл.6§2), `areaModel` (a·b, Гл.1§6), `numberLineWalk` (a+b стрелками, Гл.4§4), `carGraph` (машина+график, Гл.5§2), `plotLive` (живой y=kx / y=k/x с easing+переключателем, Гл.5§3), `thermometer` (±числа/модуль, Гл.4§1). **`stepPlayer` (DOM, не canvas)** + **`stepifyExamples(root)`** — движок в `goTo` (guarded) АВТО-превращает ВСЕ карточки «Разбор по шагам» во ВСЕХ главах в интерактивный пошаговый плеер (Назад/Дальше/Авто+точки). Тесты «анимации монтируются» (20/20) проверяют `<canvas>`/`.m6-step-view`. Брейншторм всех визуализаций: `plans/textbooks-6/PLAN_MATH_6_VISUAL.md` (16 реюзабельных компонентов + карта §→визуал). **Дополнительно сделано (компоненты Math6Anim, коммиты до 302b062):** `numberLineJumps` (a·b как прыжки, Гл.4§7), `coordGame` («поставь точку», клик по сетке, Гл.5§1), `reflectFold` (симметрия осевая/центральная, Гл.6§4/§5), `barModel` (% полоса, Гл.2§1), `setFilter` (числа сквозь фильтр свойства, Гл.3§1). **Итог: во ВСЕХ 6 главах есть canvas-анимации + stepPlayer на всех «Разборах по шагам».** Тест «анимации монтируются» проверяет `<canvas>` в Гл.1§6,2§1,3§1,4§1/4/7,5§1/2/3,6§2/4/5. Тесты math6: 20/20.
|
||||
**3D-тела ИСКЛЮЧЕНЫ** (по решению пользователя) — Гл.6§1 остаётся со статичной SVG-галереей.
|
||||
**ОПЦИОНАЛЬНАЯ ПОЛИРОВКА ЗАВЕРШЕНА (2026-06-02, коммиты 51db000 + 21c18ce):** добавлены `pieGrow` (растущие сектора, Гл.2§7 — заменил статичный Math6.pie, цвета синхронны легенде), `balanceScale` (весы a·d ? b·c, Гл.2§3, кнопка «другой пример»), `constAreaRect` (обратная проп. = постоянная площадь, Гл.2§4, ползунок x), `triangleDrag` (SVG-треугольник с перетаскиваемыми вершинами + live-классификация по сторонам/углам, штрихи равных сторон, метка прямого угла; блок «Песочница» в Гл.6§3). `vennDrag` ПРОПУЩЕН осознанно — в Гл.3§3 уже есть хороший интерактивный Math6.venn с подсветкой ∩/∪. Тесты math6: 20/20. Визуально canvas/SVG в jsdom НЕ проверить — нужен реальный браузер (глаз пользователя).
|
||||
|
||||
**ОБОГАЩЕНИЕ (2026-06-02, коммит 85c516e):** воркфлоу `math6-enrich` — 6 агентов Sonnet (по главе) добавили в каждый содержательный § карточки «Где это в жизни» (хук), «Разбор по шагам», «А знаешь ли ты?» (факт) и довели до ≥2 интерактивов. План: `plans/textbooks-6/PLAN_MATH_6_ENRICH.md`. Проверено: тесты 18/18, честный рендер (jsdom-over-HTTP с реальными defer-скриптами) — контент появляется, рантайм-ошибок нет.
|
||||
|
||||
**КРИТИЧНЫЙ БАГ ИСПРАВЛЕН (коммит fe37837):** в `math6_engine.js` вызов `init()` стоял ВЫШЕ строк `window.makeCard=…`. При defer-загрузке (readyState='interactive') ветка `else init()` срабатывала синхронно → `init→goTo→buildP1()` звал `makeCard` ДО экспорта → `ReferenceError: makeCard is not defined` → ensureBuilt catch → ВСЕ §1 показывали заглушку «Содержание готовится». jsdom-тесты баг НЕ ловили (там старт через DOMContentLoaded). Фикс: `init()` — строго ПОСЛЕ всех `window.*` экспортов; добавлен регресс-тест (init после makeCard); html учебника всегда `no-store`. ВАЖНЫЙ УРОК: при defer-движке экспортировать хелперы в window ДО запуска init.
|
||||
|
||||
**ОСТАЛОСЬ ТОЛЬКО:** выдать **доступ ученикам/классам** (хаб закрыт по умолчанию, allowlist) — это действие админа через панель или `POST /api/access/rules {content_type:'textbook',content_ref:'math-6',scope:'class',target_id,allow:1}` ([[project_content_access]]). Опционально: проверка в браузере, расширение пулов задач.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: project_optics_constructor
|
||||
description: Конструктор оптических систем в оптической скамье (BenchSim) — что это и как устроено
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: e04a2ab1-2fce-4387-9ec4-7f3f2fb6d65c
|
||||
---
|
||||
|
||||
Оптическая скамья (`frontend/js/labs/opticsbench.js`, ~4600+ строк) — это **7 режимов-вкладок**, каждый отдельный класс/canvas/панель: линза (`ThinLensSim`), зеркало (`MirrorSim`), преломление (`RefractionSim`), **Конструктор** (`BenchSim`), призма (`PrismSim`), интерференция (`InterferenceSim`), волны (`DiffractionSim`). Переключение — `obSwitchMode(mode)`.
|
||||
|
||||
**Конструктор оптических систем** (май 2026, коммиты 832efc0…1c7d8e9) — режим `freebuild`, вкладка «Конструктор» (бывш. «Цепочка линз»). Класс `BenchSim` — общий 2D-трассировщик:
|
||||
- Элементы по `xf` (0..1), центр на оси: линза (f, ap), зеркало (kind plane/concave/convex, R, ap), диафрагма (gap), экран, призма (apex, n, size), **граница сред** (n1|n2, Снеллиус+ПВО), **стеклянная пластина** (n, t, параллельный сдвиг). Источник: предмет/точка/параллель/**одиночный луч**/**лазер**, с углом прицеливания `ang`. Линза/зеркало отсекают лучи вне апертуры (виньетирование); у собирающей линзы метки F/2F.
|
||||
- Трассировка `_traceRay`: ближайший элемент по ходу → `_interact` → дальше; лимит отражений (зеркала разворачивают ход). Линза — параксиальный кик θ'=θ−y/f (фокус в x+f). Призма — тонкопризменное δ=(n−1)·A + дисперсия `_nAtWavelength(n,λ)`.
|
||||
- Белый свет (общий λ-бар скамьи, `window._obWhiteLight`): пучки по `OB_SPECTRAL`, каждый луч красится `wavelengthToRGB(wl)` → после призмы спектр. Экран ловит изображение — светящиеся пятна (`_drawScreenHits`, additive).
|
||||
- UI: динамический инспектор (`_benchUpdateUI`, `bench-list`/`bench-props`), палитра `benchAdd(type)`, `benchSelect/benchUpdate/benchRemove`, пресеты `benchPreset` (микроскоп/телескоп/проектор/зеркальная), `benchExportPng`. ВАЖНО: слайдеры свойств вызывают `updateElement`→`_redraw` (только холст), НЕ `_changed` — иначе пересборка панели ломает drag слайдера.
|
||||
- Состояние: `benchSim.getState/setState`, проброшено в `_obGetState/_obApplyState` (снимок/embed).
|
||||
|
||||
Старый `FreeBuildSim`/`freeSim` и функции `freeAddLens/freeLensF` — legacy, не используются (панель переведена на bench*). Ревью скамьи и план — `plans/OPTICS_CONSTRUCTOR.md`.
|
||||
|
||||
Бэклог: точная двухгранная призма (Снеллиус на гранях), апертурное отсечение лучей вне линзы (сейчас проходят прямо), профиль интенсивности на экране, поворот элементов.
|
||||
|
||||
Правило: при правке opticsbench.js поднимать `?v=N` у `<script src="/js/labs/opticsbench.js?v=N">`. См. [[project_stereo3d_improvements]], [[feedback_no_emoji]].
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: project_permissions_rework
|
||||
description: "Переработка ролевых прав LearnSpace (registry/role_permissions/user_permissions): Phase A+B готовы, Phase C (кастомные роли) остаётся"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 60467058-b40e-4bd9-9f7f-d1e362e8039a
|
||||
---
|
||||
|
||||
Переработка **ролевой системы прав** (отдельной от content_access — см. [[project_content_access]]).
|
||||
Это система `registry.js` (ключи прав) + `role_permissions`/`user_permissions` + middleware
|
||||
`requirePermission` (читает права ЖИВЬЁМ из БД каждый запрос) + админ-вкладка «Доступ · роли»
|
||||
(`frontend/js/admin/sections/permissions.js`) + модалка прав пользователя (`users.js`, `up-modal`).
|
||||
План: `plans/permissions-rework/PLAN.md`.
|
||||
|
||||
**Phase A + B ЗАВЕРШЕНЫ (2026-06-03), всё на master:**
|
||||
- **A1** (9ac2a61) — зависимости `requires` в реестре (questions.delete→manage, templates.public→manage,
|
||||
courses.interactive→manage, simulations.quiz→access). Право = own AND все requires. UI-каскад.
|
||||
- **A2** (b0e385b) — lint-тест `backend/tests/permissions-registry.test.js` (ключи requirePermission/perm
|
||||
есть в реестре) + метки theory/simulations переформулированы («…доступен роли»).
|
||||
- **A3** (7d474b4) — история изменений прав: `GET /api/permissions/log` (admin), кнопка на вкладке.
|
||||
- **A4** (6bd1532) — убран role-level `token_version` bump (серверное применение живое → не нужен
|
||||
массовый разлогин роли). User-level bump оставлен.
|
||||
- **B5** (0a24a66) — группы прав (поле GROUP в реестре → byRole.group), секции в UI + вкл/выкл группы.
|
||||
- **B6** (b95b639) — массово по классу: `POST /api/permissions/class/:id/bulk` (admin), всем ученикам.
|
||||
- **B7** (8b495f1) — пресеты-профили (PRESETS.student: full/focus/restricted/reset),
|
||||
`GET /api/permissions/presets` + `POST /api/permissions/class/:id/preset`; общий хелпер `applyPermsToClass`.
|
||||
- **B8** (a250d15) — временные права: миграция **053** (`user_permissions.expires_at`). Резолвер/`/me`/
|
||||
`/users/:id` игнорируют просроченные; `seedDefaults` чистит. `setUserPermission(...,days)`. В модалке
|
||||
прав пользователя — бейдж «до ДАТА» + кнопка «врем.».
|
||||
|
||||
Тесты: `permissions.test.js` 17/17, `permissions-registry.test.js` 2/2. Полный backend-набор в рамках
|
||||
baseline (3 Auth + флака «intro» chemistry8 под нагрузкой).
|
||||
|
||||
**PHASE C (ПРОИЗВОЛЬНЫЕ КАСТОМНЫЕ РОЛИ) — ЗАВЕРШЕНА И ВЛИТА В master** (2026-06-03, fast-forward
|
||||
до `b4a5b1a`, запушено в origin; ветка `feature/custom-roles` осталась локально, можно удалить).
|
||||
План: `plans/permissions-rework/PHASE_C_DESIGN.md`.
|
||||
Модель (без рефактора 111 requireRole): кастомная роль НАСЛЕДУЕТ «базовые роли» (какие встроенные гейты
|
||||
проходит) + хранит функциональную базу в `users.role` (CHECK ок) + имя в `users.custom_role` + свой набор
|
||||
прав в role_permissions под именем роли.
|
||||
- C-1 (054): таблица `roles`(name,label,base_roles,is_builtin) + `auth.effectiveRoles()`; requireRole
|
||||
сверяет пересечение с effectiveRoles(customRole||role) — встроенные роли быстрый путь, 111 гейтов не задеты.
|
||||
- C-2 (055): `users.custom_role` (ADD COLUMN, без пересборки users); `updateRole` принимает кастомную роль
|
||||
→ база=base_roles[0] + custom_role=имя; `authMiddleware`/`optionalAuth` → req.user.customRole.
|
||||
- C-3 (056): снят CHECK у role_permissions; `isEnabled(uid,permRole,baseRole,key)` = user→role_permissions
|
||||
[customRole]→фолбэк[base]→дефолт(base); getMyPermissions/getUserPermissions: roleMap база+оверлей.
|
||||
- C-4a: rolesController + `/api/roles` CRUD (admin, inline guards) + засев прав из базы; setPermission
|
||||
принимает кастомные роли (ключ по базе, хранит под именем).
|
||||
- C-4b: UI «Конструктор ролей» в `permissions.js` (#perm-roles в admin.html: создать/настроить права/
|
||||
удалить) + выпадающий список ролей у пользователя в `users.js` (optgroup «Кастомные роли»).
|
||||
Тесты: custom-roles 8/8, roles-api 5/5, permissions 17/17, full backend в рамках baseline.
|
||||
**ВЛИТО в master 2026-06-03** (push ok, сервер перезапущен на master, /api/health = 200). Кнопка «Права»
|
||||
в карточке пользователя видна всем, кроме admin (фикс b4a5b1a).
|
||||
**НЕ делались (исходный дизайн Phase C, не выбраны):** C-10 делегирование учителю, C-11 пер-классовый скоуп прав.
|
||||
|
||||
**Заметка от A2-линта:** ряд teacher-прав (`students.invite`, `sessions.reset`, `results.export`,
|
||||
`schedule.manage`, `templates.public`, `courses.interactive`) и `theory.access` НЕ enforce-ятся через
|
||||
`requirePermission` на сервере — потенциальные недогейченные точки, проверить отдельно.
|
||||
|
||||
**Гочи:** новый роут требует inline-гейт (requireRole/requirePermission), иначе pre-commit route-lint
|
||||
блокирует (был случай с /class/:id/bulk). Сервер надо перезапускать, чтобы подхватить изменения.
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
name: project_pet_assistant
|
||||
description: «Квантик-ассистент» — сквозной помощник поверх питомца; Ф0/Ф1/«Спроси» РЕАЛИЗОВАНЫ на master (правиловый движок)
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 60467058-b40e-4bd9-9f7f-d1e362e8039a
|
||||
---
|
||||
|
||||
Дизайн-доку: `plans/pet-assistant/PLAN.md`. Реализация: commit **3f8009c** (2026-06-04), на master.
|
||||
|
||||
Суть: питомец «Квантик» стал плавающим помощником (низ-слева) на всех страницах — контекстные
|
||||
подсказки, проактивные напоминания, поздравления, панель «Спроси Квантика». Движок **правиловый**
|
||||
(без LLM), правила инлайн в `frontend/js/assistant.js`.
|
||||
|
||||
Фича-гейт: **отдельный `assistant`** (commit e1cde83 — сменили с reuse 'pet'), `requireFeature('assistant')`,
|
||||
дефолт ON; админ включает/выключает в Управление→фичи (adminController allowed + games.js GAME_FEATURES,
|
||||
key 'assistant'). Включён **всем** (assistant_enabled DEFAULT 1 — личный тумблер в профиле);
|
||||
«видел» — **серверная** таблица `assistant_seen` (cross-device); ассистент **и на учебнике** (через
|
||||
DEEPLINK_INJECT в server.js); тон **консервативный** (дневной лимит 2, кулдауны, «не показывать»);
|
||||
«Спроси» — **поиск по FAQ + точка расширения под локальную модель** (в `ask()` контроллера).
|
||||
|
||||
Файлы: миграция `062_assistant.sql` (assistant_enabled + assistant_seen); `assistantController.js`
|
||||
(FAQ инлайн — `backend/src/data/` в .gitignore!) + `routes/assistant.js`; mount `/api/assistant`
|
||||
под `requireFeature('pet')`; `js/api.js` (assistantContext/Seen/Dismiss/Settings/Ask); загрузчик в
|
||||
`js/sidebar.js` (как flashcard-fab); тумблер в `profile.html` («Настройки» → prefAssistant).
|
||||
Эндпоинты: GET /context, POST /seen, /dismiss, /ask, PATCH /settings. Лицо — `pet-sprite.js`,
|
||||
данные — `/api/pet` + `/api/assistant/context` (dueCards, homework).
|
||||
|
||||
Сделано: Ф0 (каркас+контекстные подсказки) + Ф1 (проактив: домашка/карточки/серия/квест/
|
||||
activeLesson «продолжи урок» + поздравления левелап/серия) + Ф2 (коачмарк-тур новичка по разделам,
|
||||
офер на дашборде, повтор Assistant.tour()) + Ф3-lite (FAQ-«Спроси»). Тур-правило id 'onboarding'
|
||||
в assistant_seen. activeLesson (commit 9baaca7) — запрос как «продолжить чтение» из courseController.
|
||||
+ Контент-апгрейд (commit c33295e): контекстные подсказки на ВСЕ разделы (PAGE_HINTS),
|
||||
«Совет дня» (tip-daily, дашборд), FAQ ~10→~50, «Спроси» ищет и по платформе (LS.globalSearch),
|
||||
умный проактив weakSubject (слабый предмет по test_sessions, в /api/assistant/context) +
|
||||
daily-plan (из квестов+карточек). LLM подключена (commit 9dbc044): ask() вызывает OpenAI-совместимую модель с грунтовкой по топ-FAQ,
|
||||
source:'model', таймаут 12с, откат на FAQ при ошибке/без ключа. Конфиг ENV в backend/.env(.example):
|
||||
ASSISTANT_LLM_URL (дефолт Groq chat/completions), ASSISTANT_LLM_KEY (пусто → FAQ), ASSISTANT_LLM_MODEL
|
||||
(дефолт llama-3.3-70b-versatile); локальный Ollama без ключа поддержан (localhost → зовётся без Bearer).
|
||||
АКТИВНО (2026-06-04): подключён **Google Gemini** через OpenAI-совместимый эндпоинт
|
||||
(ASSISTANT_LLM_URL=https://generativelanguage.googleapis.com/v1beta/openai/chat/completions),
|
||||
рабочая модель **gemini-2.5-flash** (ключ в backend/.env, не в гите). Гочи по моделям на этом ключе:
|
||||
gemini-2.0-flash / -lite → 429 limit:0 (нет free-квоты); gemini-1.5-* → 404; gemini-2.5-flash / gemini-flash-latest → 200.
|
||||
Ключ валиден (auth ок). Сменить провайдера/модель — только через backend/.env + рестарт.
|
||||
Возможности-апгрейд (commit 479c621): ответы модели рендерятся markdown + KaTeX (ленивая загрузка
|
||||
katex с jsdelivr; модель просим LaTeX $...$); ask принимает context; «Объяснить выделенное» (запоминаем
|
||||
selection на mouseup) и «Объяснить/Конспект параграфа» на учебнике (getPageContext по .sec.active);
|
||||
«Флешкарты из параграфа» → POST /api/assistant/flashcards (модель→JSON, есть починка обрезанного,
|
||||
max_tokens 1400) → колода через LS.fcCreateDeck/fcAddCard; репетитор на экзамене — кнопка «Спросить
|
||||
Квантика» в task-card.js (tc-ask) → Assistant.ask(условие+ответ+решение). Экспорт Assistant.ask(q,context)
|
||||
и explainSelection(). Гочи: на этом Gemini-ключе free-квота есть только у gemini-2.5-flash; placeholder
|
||||
в renderRich — @@M..@@ (не цифры-в-пробелах, иначе коллизии).
|
||||
Админ-панель + чат (commit dc073e2): конфиг LLM теперь в **app_settings** (assistant_llm_url/key/model),
|
||||
правится из Управление→игры (карточка «Помощник Квантик — модель»: пресеты Gemini/Groq/OpenRouter/Ollama,
|
||||
URL/модель/ключ, Сохранить/Проверить/Очистить, статус). Эндпоинты GET/PUT/POST /api/admin/assistant(/test)
|
||||
admin-only. llmConfig() читает app_settings→ENV→дефолт; нет ключа → FAQ-режим. Текущий Gemini-ключ пока
|
||||
в backend/.env (DB пусто → берётся ENV; можно перенести в БД через админку). «Спроси» — многоходовой чат
|
||||
(history последние 6 реплик уходят модели, лента сообщений, «Очистить»).
|
||||
RAG+кэш+учитель (commit 2252bbd, миграция 063): **RAG** — индексатор backend/scripts/index-textbooks.js
|
||||
→ таблица textbook_chunks; ask() подмешивает релевантные куски (LIKE-скоринг, ≥2 слова). ГОЧА: бол-во
|
||||
учебников рендерят текст ЧЕРЕЗ JS-движки → в статическом HTML только заголовки §, поэтому индекс берёт
|
||||
только статично-текстовые (≈132 чанка: chemistry, часть physics); JS-рендеримые покрываются контекстом
|
||||
страницы (getPageContext читает отрендеренный DOM). Полное покрытие = headless-рендер — СДЕЛАНО (commit 0119ea0): scripts/index-textbooks-headless.js
|
||||
(puppeteer-core + системный Chrome, служебный JWT в localStorage т.к. /textbook требует логина) рендерит
|
||||
учебники через локальный сервер, кликает §, забирает рендерный текст движков. Прогон: 87/107 книг, индекс
|
||||
132→**746 чанков** (physics-9 и др. JS-учебники теперь покрыты). npm: index:textbooks:full. Сервер не
|
||||
требует рестарта — ragContext читает БД на каждом запросе.
|
||||
|
||||
Батч 4 фич (commit 4224a22, миграция 064): **Источники** — ragContext отдаёт sources (slug/section/section_ref),
|
||||
под ответом ссылка «по учебнику X, §N» на /textbook/slug#sec-<ref>; section_ref заполняет headless-индексатор
|
||||
(psel-card data-id); статический индексатор больше НЕ делает delAll (per-book — не затирает headless).
|
||||
**Режим-наставник**: ask(mode: answer/hint/check) + промпт; в «Спроси» переключатель из 3 кнопок; на карточке
|
||||
экзамена (task-card.js) кнопка «Подсказка» (mode hint) рядом со «Спросить Квантика»; Assistant.ask(q,ctx,{mode}).
|
||||
**Оценка**: лайк/дизлайк под ответом (assistant_feedback, POST /assistant/feedback) + сводка в админке (up/down/recent).
|
||||
**Утренний бриф**: rule 'brief' на дашборде до 12:00 (PET.weeklyXP «N из 5 дн» + план), daily-plan сдвинут на день.
|
||||
НЕ сделано: цели недели (явная установка) + напоминания по расписанию (нужен планировщик/push); голосовой ввод/TTS;
|
||||
генератор заданий учителю; авто-cron на index:textbooks:full.
|
||||
|
||||
Мета-фильтр + тумблер экзамена (commit 961504b): вопросы про модель/нейросеть/провайдера/системный промпт
|
||||
отбиваются шаблоном META_RE (саморефренция ты/тебя/твой РЯДОМ с термином модель/gpt/промпт — не блокирует
|
||||
«модель атома/газа») + запрет в системном промпте. Кнопки «Подсказка»/«Спросить Квантика» на карточках
|
||||
экзамена по умолчанию СКРЫТЫ (assistant_exam_buttons='0'); тумблер в админке → /context examButtons →
|
||||
assistant.js вешает html.asst-exam-on (CSS показывает .tc-asst-btn). Память чата: последние 6 реплик уходят
|
||||
модели, живёт в памяти вкладки до «Очистить»/reload. §-ссылки источников: section_ref у 531/746 чанков (после
|
||||
headless-reindex); у остальных ссылка ведёт на главу.
|
||||
|
||||
Лимит-UX + отдельный раздел админки (commit 7830084): «не помнит» оказалось free-лимитом Gemini (HTTP 429,
|
||||
≈20 req/min). callLLM теперь возвращает {text,error}; ask отдаёт source:'limit' («много запросов, подожди,
|
||||
память не потеряется») или 'error'; фронт показывает это и НЕ ломает историю (pop неудачного вопроса). Многоходовость
|
||||
работала и раньше — глушил лимит. В окне «Спроси» добавлено пояснение про память (≈6 реплик = рабочая память) +
|
||||
аватар Квантика в шапке, окно шире/мягче. Управление вынесено в **отдельный раздел админки «Помощник Квантик»**
|
||||
(frontend/js/admin/sections/assistant.js; AdminSections.assistant; ROUTE_TO_SECTION+ADMIN_ONLY_TABS+btn-tab-assistant;
|
||||
из games.js конфиг и фича-запись удалены) — там системный вкл/выкл (feature 'assistant' через /api/admin/features) +
|
||||
модель/ключ/тест/RAG/кнопки экзамена/счётчик/качество. ВАЖНО: free-квота Gemini gemini-2.5-flash = **20 запросов В СУТКИ** (quotaId GenerateRequestsPerDayPerProjectPerModel-FreeTier),
|
||||
остальные модели Gemini на ключе — limit:0. Это мизер → постоянный 429. Решение: сменить провайдера на **Groq**
|
||||
(free-тариф щедрый, ~тысячи/день, 30 RPM) — создать ключ console.groq.com, в админ-разделе «Помощник Квантик»
|
||||
выбрать пресет Groq + вставить ключ; ИЛИ включить billing Gemini; ИЛИ локальная Ollama. Провайдер меняется в админке.
|
||||
|
||||
Мульти-провайдер (commit e2bff24): конфиг = список провайдеров app_settings.assistant_providers (JSON
|
||||
[{id,name,url,model,key}]) + assistant_active. llmConfig=активный; providersOrdered=активный первым + остальные
|
||||
с ключом; callLLMFailover перебирает их при 429/timeout/network/http (askModel и flashcards идут через него) —
|
||||
второй ключ авто-подхватывает при исчерпании квоты первого. Legacy assistant_llm_* мигрируются в список при
|
||||
первом GET админки. Админ-раздел «Помощник Квантик»: список провайдеров (радио=активный, Тест/Изменить/Удалить
|
||||
по каждому) + форма с пресетами. Эндпоинты POST/DELETE /admin/assistant/provider(/:id), POST /admin/assistant/active.
|
||||
Решение лимита Gemini (20/сутки): добавить Groq вторым провайдером — failover сам переключит.
|
||||
ГОЧА: **Groq гео-заблокирован в Беларуси** («Access denied» на console.groq.com) — ключ не создать без VPN.
|
||||
Железо машины подходит для ЛОКАЛЬНОЙ модели: 32 ГБ ОЗУ, GTX 1080 8 ГБ VRAM (WMI врёт 4 ГБ) → Qwen2.5-7B через
|
||||
Ollama идеально. Выбор «локально vs Gemini billing vs OpenRouter» пока НЕ сделан (вопрос прерван). Failover-
|
||||
уведомление (commit aac1240): callLLMFailover пишет app_settings.assistant_failover {failedName,servedName,reason,at};
|
||||
при успехе активного снимается; админ-раздел показывает баннер «провайдер X недоступен — работаю на Y» / «все недоступны».
|
||||
|
||||
ПОДКЛЮЧЁН Kilo Code (работает из Беларуси!) как АКТИВНЫЙ провайдер: URL
|
||||
**https://kilocode.ai/api/openrouter/chat/completions**, ключ — JWT (в app_settings, не в репо), модель
|
||||
**nvidia/nemotron-3-ultra-550b-a55b:free** (бесплатно, чистый русский + LaTeX). Gemini остался вторым (failover).
|
||||
Гочи Kilo на этом ключе: публичные `:free` (deepseek/mistral) → 404 «No endpoints»; платные → 401 «need to sign in»
|
||||
(нет кредитов); kilo-auto/free → пустой ответ; рабочие бесплатные: nvidia/nemotron-3-ultra-550b-a55b:free и
|
||||
openrouter/owl-alpha. Наш callLLM (без HTTP-Referer) Kilo принимает. nemotron — reasoning-модель → в промпт добавлено
|
||||
«не выводи рассуждения вслух» (commit d1be2c1). Список моделей Kilo: GET .../api/openrouter/models (342 шт).
|
||||
Админка (games.js, карточка): тумблер RAG, «Переиндексировать», число фрагментов. **Кэш** assistant_cache
|
||||
(7 дней, только вопросы без контекста/истории) + **счётчик** assistant_usage (ИИ/кэш/FAQ, в админке).
|
||||
**Учитель**: role в /context, доп. системный промпт для teacher/admin + teacher-чипы в «Спроси».
|
||||
**Ключ перенесён в БД** (app_settings assistant_llm_url/key/model), из backend/.env удалён — конфиг
|
||||
теперь только через админку. НЕ сделано: headless-RAG для JS-учебников; голосовой ввод; редактор промпта.
|
||||
Связано: [[reference_quick_lesson]] [[reference_student_materials]] [[feedback_no_emoji]].
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: project_phys7_status
|
||||
description: Физика 7 — контент всех 5 глав ГОТОВ (рендерится из widget-файлов); «В разработке» были ложными заглушками; не хватает только Шпаргалок
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 52938fe6-1430-4329-808c-f4e6ad780a81
|
||||
---
|
||||
|
||||
Учебник «Физика 7» (`frontend/textbooks/physics_7_ch1..5.html`) по контенту ПОЛНОСТЬЮ готов, хотя на вид кажется скелетом. Все ~42 параграфа + финалы рендерят полноценный контент (20–33 тыс. символов: теория + интерактивы).
|
||||
|
||||
**Архитектура (отличается от физики 8):** контент вынесен во внешние JS — `frontend/js/phys7_chN_widgets.js` (~600 КБ суммарно), экспорт `window.PHYS7_CHN_WIDGETS = { pN: fn, finalN: fn }`. Страница диспетчеризует через `ensureBuilt(id)` → `W[id]()`, перед сборкой удаляя `.placeholder`. (У физики 8 наоборот — `build_pN` инлайнятся прямо в странице.)
|
||||
|
||||
**«Заглушки» были ложными:** боковая Шпаргалка (`SIDEBARS`) и Подсказка (`TIPS`) были захардкожены как «В разработке»/«Скелет главы готов» со времён Phase 0 — убраны 2026-06-01 (commit `03ed4bb`). В теле параграфов остались статические `.placeholder` («появится в ближайших фазах»), но они авто-удаляются в рантайме и не видны.
|
||||
|
||||
**Шпаргалки наполнены** (2026-06-01, commit `c6835cf`): во всех 5 главах `SIDEBARS` теперь явный объект с реальными rows (47 шпаргалок: 42 § + 5 финалов, формат как в физике 8 — `{title, rows:[[ключ, значение]...]}`, KaTeX в `$...$`). buildSidebar рендерит карточку при `sb.rows.length`. Учебник физики 7 теперь функционально полный. См. [[project_status]].
|
||||
@@ -0,0 +1,206 @@
|
||||
---
|
||||
name: LearnSpace — полная карта реализованных функций
|
||||
description: Что уже сделано в проекте: все страницы, API, таблицы БД, инструменты доски, стек, деплой
|
||||
type: project
|
||||
originSessionId: 1959f491-c6c4-4d6b-9081-0b09298d1699
|
||||
---
|
||||
# LearnSpace — реализованный функционал (апрель 2026)
|
||||
|
||||
**Why:** Чтобы не переоткрывать то, что уже есть, при планировании новых фич.
|
||||
**How to apply:** Перед реализацией любой фичи — сверяться, чтобы не дублировать.
|
||||
|
||||
---
|
||||
|
||||
## Frontend pages (43 HTML-файла в frontend/)
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|-----------|
|
||||
| login.html | Split-layout авторизация, canvas neural-network анимация |
|
||||
| dashboard.html | Главная ученика — задания, прогресс, gamification |
|
||||
| admin.html | Панель администратора |
|
||||
| profile.html | Профиль пользователя |
|
||||
| classes.html | Google Classroom-стиль карточки классов |
|
||||
| board.html | Лента класса (анонсы, задания, активность) |
|
||||
| classroom.html | Онлайн-урок (доска + чат + видео/аудио) |
|
||||
| live-quiz.html | Live-викторина в реальном времени |
|
||||
| test-run.html | Прохождение теста |
|
||||
| test-result.html | Результат теста |
|
||||
| question-bank.html | Банк вопросов |
|
||||
| homework.html | Задания студента |
|
||||
| course.html | Прохождение курса |
|
||||
| lesson.html | Просмотр урока |
|
||||
| lesson-editor.html | Редактор уроков с блоками |
|
||||
| theory.html | Теоретические материалы |
|
||||
| library.html | Библиотека файлов |
|
||||
| analytics.html | Аналитика/отчёты |
|
||||
| flashcards.html | Карточки с интервальным повторением |
|
||||
| knowledge-map.html | Граф знаний |
|
||||
| crossword.html | Кроссворд |
|
||||
| hangman.html | Виселица |
|
||||
| biochem*.html (5) | Интерактивная биохимия: молекулы, реакции, пути |
|
||||
| red-book*.html (4) | Красная книга: виды, биомы, экосистемы, игры |
|
||||
| collection*.html (2) | Коллекции предметов |
|
||||
| gradebook.html | Журнал оценок |
|
||||
| parent.html | Кабинет родителя |
|
||||
| pet.html | Виртуальный питомец |
|
||||
| lab.html | Интерактивные лабораторные работы (30+ симуляций) |
|
||||
| 403/404/500.html | Страницы ошибок |
|
||||
|
||||
---
|
||||
|
||||
## Backend API (28 групп маршрутов)
|
||||
|
||||
```
|
||||
/api/auth — JWT авторизация, регистрация, профиль
|
||||
/api/subjects — Предметы
|
||||
/api/sessions — Тестовые сессии
|
||||
/api/admin — Управление платформой, feature flags
|
||||
/api/questions — Банк вопросов
|
||||
/api/classes — Классы
|
||||
/api/assignments — Задания, дедлайны
|
||||
/api/files — Загрузка файлов, папки
|
||||
/api/tests — Тесты/квизы
|
||||
/api/notifications — Уведомления
|
||||
/api/permissions — RBAC
|
||||
/api/submissions — Сдача работ, оценки
|
||||
/api/courses — Курсы (теория)
|
||||
/api/lessons — Уроки с блоками
|
||||
/api/gamification — XP, уровни, ачивки, стрики
|
||||
/api/shop — Виртуальный магазин, монеты
|
||||
/api/templates — Шаблоны заданий
|
||||
/api/bookmarks — Закладки
|
||||
/api/search — Поиск контента
|
||||
/api/flashcards — Флэшкарты со spaced repetition
|
||||
/api/settings — Настройки
|
||||
/api/analytics — Аналитика и отчёты
|
||||
/api/live — Live-квизы (real-time)
|
||||
/api/classroom — Онлайн-урок (SSE, доска, чат, WebRTC)
|
||||
/api/games — Игры (виселица, кроссворд)
|
||||
/api/knowledge-map — Граф знаний
|
||||
/api/pet — Виртуальный питомец
|
||||
/api/collection — Коллекционирование
|
||||
/api/red-book — Красная книга
|
||||
/api/biochem — Биохимия
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Classroom API (детально)
|
||||
|
||||
- POST/GET/DELETE сессий
|
||||
- JOIN/LEAVE участников, attendance log
|
||||
- Чат: отправка, получение, реакции, закрепление, загрузка файлов
|
||||
- Strokes: batch save, load (с пагинацией + since_seq), update, delete, preview (SSE)
|
||||
- Страницы: add, change_current, set_template, clear
|
||||
- Поднятая рука: raise/lower/list
|
||||
- Разрешения рисования: grant/revoke per user
|
||||
- WebRTC: signaling relay, cursor broadcast, mute, screen share
|
||||
- Notes: get/save per user per session
|
||||
- Templates: save/load session как шаблон
|
||||
|
||||
---
|
||||
|
||||
## БД — 76 таблиц SQLite (better-sqlite3, sync)
|
||||
|
||||
Ключевые группы:
|
||||
- **users** + role_permissions + user_permissions
|
||||
- **test_sessions** + session_questions + user_answers
|
||||
- **subjects** + topics + questions + options + tests + test_questions
|
||||
- **classes** + class_members
|
||||
- **classroom_sessions** + classroom_pages + classroom_strokes + classroom_chat + classroom_chat_reactions + classroom_attendance + classroom_invites + classroom_draw_permissions + classroom_notes
|
||||
- **courses** + course_sections + lessons + lesson_blocks + lesson_progress + lesson_notes + class_courses
|
||||
- **assignments** + assignment_sessions + assignment_templates + submissions + submission_log
|
||||
- **files** + file_access + folders + folder_access
|
||||
- **xp_log** + achievements + user_achievements + daily_goals + challenges
|
||||
- **announcements** + notifications + bookmarks
|
||||
- **live_sessions** + live_answers
|
||||
- **shop_items** + user_purchases
|
||||
- **flashcard_decks** + flashcard_cards + flashcard_reviews
|
||||
- **bio_elements/molecules/reactions/...** (5 биохим-таблицы)
|
||||
- **rb_species/habitats/groups/...** (9 красная книга)
|
||||
- **app_settings** + error_log + admin_audit_log
|
||||
|
||||
---
|
||||
|
||||
## Whiteboard (frontend/js/whiteboard.js ~3200 строк)
|
||||
|
||||
### Инструменты рисования
|
||||
- Pencil (Catmull-Rom сглаживание)
|
||||
- Highlighter (полупрозрачный маркер)
|
||||
- Laser (без сохранения)
|
||||
- Eraser
|
||||
- Connector (линии со стрелками)
|
||||
- Sticky notes
|
||||
- Text (inline editing)
|
||||
- Image (вставка + upload)
|
||||
- Formula / LaTeX (KaTeX, modal editor с категориями)
|
||||
- Table (интерактивная)
|
||||
- Coordinate system (с графиками функций, парсер выражений)
|
||||
- Number line (для неравенств, точки + интервалы)
|
||||
- Compass: трёхфазная state machine (idle → setting-radius → waiting-arc → drawing-arc), сохраняется как `{cx, cy, radius, arcStart, arcSweep, color, lineWidth, showLegs}`, live preview в `_renderDynamic` с ногами компаса и меткой угла
|
||||
|
||||
### Shapes (11)
|
||||
rect, ellipse, line, arrow, triangle, diamond, hexagon, star, roundedrect, callout, connector
|
||||
|
||||
### Инструмент выделения
|
||||
- Move + resize всех объектов (bbox handles)
|
||||
- Rotation handle (purple) для объектов
|
||||
- Lasso multi-select (резиновая рамка)
|
||||
- Shift+click добавить к выделению
|
||||
- Copy/Paste с offset
|
||||
- Snap guides при перемещении
|
||||
- Delete / Bring to front / Send to back
|
||||
|
||||
### Zoom / Pan
|
||||
- Wheel zoom (к курсору)
|
||||
- Space+drag = pan
|
||||
- Ctrl+0/+/- hotkeys
|
||||
- clampPan() — ограничение выхода за пределы
|
||||
- Minimap: 192×108 overlay bottom-right (показывается при zoom>1)
|
||||
- Viewport indicator на minimap; клик/drag = прыжок
|
||||
|
||||
### Оверлеи (не сохраняются)
|
||||
- Ruler: вращение (drag ↺), resize (drag ↔), свойства-панель (угол + длина)
|
||||
- Protractor: вращение, resize (radius), свойства-панель
|
||||
|
||||
### Прочее
|
||||
- Двухслойный canvas: static (strokes) + dynamic (selection/guides/live)
|
||||
- Шаблоны страниц: blank, grid, lined, dots, coordinate
|
||||
- Multi-page с thumbnail sidebar (renderThumbnail)
|
||||
- Export PNG (с сохранением zoom/pan)
|
||||
- Auto-measurements (длины/углы/площадь для shape)
|
||||
- Real-time sync: SSE + HTTP polling (since_seq)
|
||||
- Live strokes preview через /stroke-preview
|
||||
- Cursor broadcast (teacher position visible to students)
|
||||
|
||||
### Темы доски (4)
|
||||
- **Chalkboard** (по умолчанию): зелёный (#213d26), меловая текстура, горизонтальные смазки
|
||||
- **Blackboard** (классная): тёмно-синий (#1a1a2e), диагональная текстура, chalk-grain
|
||||
- **Corkboard** (пробка): коричневый (#7a5c1e), диагональные волокна, случайные узлы-пятна
|
||||
- **Whiteboard** (маркерная): светло-серый градиент, minimal grain, тёмные линии шаблонов
|
||||
- Переключатель: `setBoardTheme(name)` + `wbSetBoardTheme()` + `<select id="wb-theme-select">`
|
||||
- Текстуры кешируются в `_bgNoiseCache` (Map по имени темы)
|
||||
|
||||
---
|
||||
|
||||
## Tech stack
|
||||
|
||||
- **Backend**: Node.js 22, Express 4.18, better-sqlite3 (sync), JWT, bcryptjs, multer, sharp, compression
|
||||
- **Frontend**: Vanilla JS ES6+, HTML/CSS без бандлера, Canvas API, SSE, WebRTC
|
||||
- **Иконки**: inline SVG `.ic` класс (НЕ эмоджи), Lucide CDN на некоторых страницах
|
||||
- **Шрифты**: Google Fonts (Unbounded, Manrope)
|
||||
- **Деплой**: Docker multi-stage Alpine, docker-compose, tini init, 3 named volumes
|
||||
- **Репо**: https://git.dolgolyov-family.by/maxim.dolgolyov/Learn_System (ветка master)
|
||||
|
||||
---
|
||||
|
||||
## Env vars (backend/.env.example)
|
||||
|
||||
```
|
||||
PORT=3000
|
||||
JWT_SECRET=...
|
||||
JWT_EXPIRES_IN=7d
|
||||
CLIENT_ORIGIN=http://localhost:3000
|
||||
DB_PATH=/app/backend/data/learnspace.db
|
||||
UPLOADS_DIR=/app/backend/uploads
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: project_stereo3d_improvements
|
||||
description: Симуляция «Стереометрия 3D» — итог ревью+апгрейда (5 фаз) и deep-link конвенция для учебников
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: e04a2ab1-2fce-4387-9ec4-7f3f2fb6d65c
|
||||
---
|
||||
|
||||
Симуляция Стереометрии 3D (`frontend/js/labs/stereo.js`, класс StereoSim на Three.js, панель `#sim-stereo` в lab.html) прошла ревью и апгрейд в 5 фаз (май 2026, коммиты 8af8596…ccfb611):
|
||||
|
||||
- Фаза 0: render-on-demand (`_invalidate`/`_needsRender`, loop засыпает), `_pauseAllSims()` в lab-init паузит фоновые rAF-симы при переключении, pointer/touch на canvas с capture, `webglcontextlost`+`dispose()`, рекурсивный `_clearGroup`.
|
||||
- Фаза 1: инерция орбиты, pan (ПКМ/СКМ/Shift, 2 пальца), overlay-тулбар (сброс/пресеты Изо·Спереди·Сбоку·Сверху/спин/fullscreen/скриншот PNG).
|
||||
- Фаза 2: аналитические сечения кривых `_sliceCurvedByNormal()` (окружность/эллипс вместо сэмплинга), `_edgePickNDC()` пикинг рёбер, HiDPI `_makeTextSprite`.
|
||||
- Фаза 3: live-readout overlay `#stereo-readout` (тип/S/P/измерение через `info().readout`), `_raycastFace()` точки на гранях, подписи вершин сечения K,L,M…
|
||||
- Фаза 4: подписи осей X/Y/Z, свечение вершин, контраст рёбер.
|
||||
- Фаза 5: deep-link + клавиатура (a11y).
|
||||
- Фаза 6 (`3801d0c`): построение сечения «по следам» (метод следов), путь (b) — надёжный полигон + аналитический след `_traceLine()` (π∩основание y=0) и вспом. точки `_auxiliaryPoints()` (продление сторон до следа). Настоящий пошаговый `_drawSection3PStep` (6 подписанных шагов, финал скрыт до шага 5), подписи в `#sect3p-hint`. Только тела с основанием (`_hasBase`: куб/параллелепипед/призма/пирамида/усеч.пир/тетраэдр). Включается тумблером «Пошагово» в блоке «Сечение через 3 точки» + кнопки Вперёд/Назад.
|
||||
|
||||
**Deep-link фигуры из учебников** (не очевидно из кода): открыть конкретное тело можно через `openSim('stereo:<figure>')` ИЛИ ссылкой `/lab?stereofig=<figure>#sim/stereo`. Допустимые `<figure>`: cube, parallelepiped, prism, pyramid, truncpyramid, tetrahedron, octahedron, icosahedron, dodecahedron, cylinder, cone, trunccone, sphere. Сделано без правки общего hash-роутера (lab-glue.js) намеренно.
|
||||
|
||||
**Бэклог** (в `plans/STEREO_3D_IMPROVEMENT.md`): дробление 3900-строчного файла на модули (отложено пользователем); полное «построение сечения по следам»; подсветка грани по ховеру (нужен точный raycast логических граней, не centroid); zoom-to-cursor; readout углов; градиентный фон в скриншоте.
|
||||
|
||||
Правило проекта: при правке stereo.js поднимать `?v=N` у `<script src="/js/labs/stereo.js?v=N">` в lab.html. См. [[feedback_sims_admin_sync]], [[feedback_no_emoji]].
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: Whiteboard improvement roadmap
|
||||
description: 7-phase plan to upgrade the interactive whiteboard from basic to professional-grade educational tool
|
||||
type: project
|
||||
originSessionId: 1959f491-c6c4-4d6b-9081-0b09298d1699
|
||||
---
|
||||
7 фаз улучшения доски LearnSpace, утверждено 2026-04-11. Реализацию выполняет Sonnet.
|
||||
|
||||
**Фаза 1** (L): Универсальное выделение (select для ВСЕХ штрихов — фигуры, карандаш, коннекторы, не только объекты) + лазерная указка + маркер (highlighter) + copy/paste для всех типов.
|
||||
|
||||
**Фаза 2** (L): Экспорт PNG + шаблоны страниц (blank/grid/lined/coordinate/dots, поле template в classroom_pages) + миниатюры страниц (sidebar 192x108).
|
||||
|
||||
**Фаза 3** (M): Стили линий (solid/dashed/dotted) + расширенная палитра (12 цветов, 5 толщин, dropdown) + opacity slider для штрихов.
|
||||
|
||||
**Фаза 4** (XL, зависит от Ф1): Двухслойный canvas (статический + динамический, dirty-region) + мульти-выделение (лассо, Shift+Click, _selectedIds: Set) + snap & alignment guides.
|
||||
|
||||
**Фаза 5** (XL): Координатная система (объект-штрих с осями/разметкой) + графики функций (рекурсивный descent parser y=f(x)) + линейка и транспортир (overlay, не сохраняются).
|
||||
|
||||
**Фаза 6** (XL, лучше после Ф4): Zoom & Pan (матрица трансформации, Ctrl+Scroll, Space+Drag) + расширяемый холст (за пределы 1920x1080).
|
||||
|
||||
**Фаза 7** (XL): Запись/воспроизведение урока (timeline player, таблица classroom_recording) + PDF-импорт (pdf.js CDN) + справка по горячим клавишам + accessibility (ARIA, keyboard nav).
|
||||
|
||||
**Why:** Текущая доска функциональна, но не хватает базовых UX-паттернов (select для всех штрихов, пунктирные линии, экспорт) и образовательных инструментов (координаты, графики).
|
||||
|
||||
**How to apply:** Фазы 1-3 независимы, делать в любом порядке. Фаза 4 зависит от 1. Фаза 6 — после 4. Фаза 7 автономна. Полный план — в plan file `bubbly-booping-harp.md`.
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: reference_exam_textbook_links
|
||||
description: "Как устроена привязка задач экзамена math9 к § учебников (per-task классификатор, deep-link), и как её перегенерировать"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: 60467058-b40e-4bd9-9f7f-d1e362e8039a
|
||||
---
|
||||
|
||||
Связь «задание экзамена → § учебника» (фича «Учить тему» в exam-prep). Сделано 2026-06-03.
|
||||
|
||||
**Модель связи (двухуровневая):**
|
||||
- Per-task: `exam_tasks.textbook_slug` + `exam_tasks.textbook_paragraph` (миграция 057). Контроллер
|
||||
`backend/src/routes/exam-prep.js` (`shapeTask`/`/variants/:n/tasks`) ПРЕДПОЧИТАЕТ task-level;
|
||||
фолбэк — subtopic-уровень `exam_topics.textbook_slug/paragraph` (миграции 028 + фикс 058).
|
||||
- Фронт `frontend/js/exam-prep/task-card.js` строит ссылку `/textbook/<slug>#sec-p<N>`.
|
||||
|
||||
**Классификатор (эвристика, детерминированный):** `backend/scripts/tag-exam-textbook.js`
|
||||
- Карта `subtopic → кандидатные §` + keyword-скоринг по тексту задачи И вариантам ответа (`opts_json`,
|
||||
формат пар `[label, html]`). Требует совпадения >0, иначе берётся явный fallback (последнее правило).
|
||||
- Таксономия §: `backend/scripts/exam-textbook-sections.json` (НЕ в `data/` — тот gitignore!),
|
||||
генерится `node backend/scripts/gen-exam-textbook-sections.js` из `frontend/textbooks/*.html`.
|
||||
Перегенерировать при изменении § учебников, затем перезапустить классификатор.
|
||||
- Запуск: `node backend/scripts/tag-exam-textbook.js --exam math9 [--dry-run] [--report]`.
|
||||
- Результат: **784/800 (98%)** размечено; 70 на хабах math-5/6 (движковые, без статич. §), 16 NULL
|
||||
(чисто-формульные theory → фолбэк на subtopic).
|
||||
|
||||
**Готчи нумерации § (важно для (slug,para)):** algebra-7/8/9 и geometry-7/9 — сквозная нумерация
|
||||
`sec-pN`; **geometry-8 — ПОГЛАВНАЯ** (каждая глава заново `sec-p1`); **math-5/6 рендерятся движком**
|
||||
`math6_engine.js` (нет статич. `sec-pN`, линкуются на уровне главы, para=null). Экзамен 9 кл. покрывает
|
||||
программу 5–9, поэтому ссылки ведут в учебники 5–9 (см. [[reference_textbook_sources]]).
|
||||
|
||||
**Deep-link был СЛОМАН, починен:** статич. страницы algebra/geometry игнорировали `location.hash`
|
||||
(init всегда `goTo('p10')`), textbook-tracker матчил только `#pN`. Решение: `server.js` всегда инжектит
|
||||
`frontend/js/textbook-deeplink.js` в `/textbook/:slug` (и embed) — по `#sec-pN`/`#pN` кликает
|
||||
`.psel-card[data-id]` (фолбэк `.para-pill[data-para]`→goTo→scrollIntoView). Универсально, идемпотентно.
|
||||
|
||||
План/находки: `plans/exam-textbook-links/PLAN.md` + `taxonomy.md`. Тесты:
|
||||
`backend/tests/exam-textbook-links.test.js` (9/9). Сделано Sonnet (фазы 2–6) + Opus-ревью (фикс
|
||||
классификатора, навигация, перенос таксономии из gitignore).
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: reference_quick_lesson
|
||||
description: «Быстрый урок» — одиночный урок без курса через скрытый личный курс-контейнер (is_personal)
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: 60467058-b40e-4bd9-9f7f-d1e362e8039a
|
||||
---
|
||||
|
||||
Одиночного урока без курса в системе нет: `lessons.course_id NOT NULL`, `POST /api/lessons`
|
||||
требует courseId. Решено через **личный курс-контейнер** (сделано 2026-06-03, commit 6be8a50).
|
||||
|
||||
- Миграция 059: `courses.is_personal INTEGER DEFAULT 0` (ADD COLUMN).
|
||||
- `POST /api/lessons/quick` (teacher/admin, `lessonController.quickLesson`): get-or-create
|
||||
контейнер `WHERE created_by=? AND is_personal=1` (subject_slug='personal', title='Мои материалы',
|
||||
is_published=1, один на учителя) → создаёт урок → `{lessonId, courseId}`.
|
||||
- Фронт: кнопка «Быстрый урок» в каталоге `theory.html` (рядом с «Новый курс», видна
|
||||
teacher/admin) → POST /quick → редирект `/lesson-editor.html?id=<lessonId>`.
|
||||
- `courseController.list` СКРЫВАЕТ `is_personal=1` из каталога для всех, кроме владельца
|
||||
(`AND (c.is_personal=0 OR c.created_by=?)`; студентам — всегда `is_personal=0`).
|
||||
- Учитель видит свои быстрые уроки как курс «Мои материалы» (открыв его в каталоге).
|
||||
- Доступ ученикам: контейнер опубликован, но урок надо ОПУБЛИКОВАТЬ (per-lesson) + доступ
|
||||
к курсу-контейнеру идёт через обычный content_access/класс (см. [[project_content_access]]).
|
||||
Standalone-урок на уровне схемы (course_id NULL) — НЕ делали (был выбран этот лёгкий путь).
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: reference_sqlite_node
|
||||
description: "БД-стек — приложение использует встроенный node:sqlite, а не better-sqlite3; путь к живой БД"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: a705e035-e600-43a2-b98c-197923986186
|
||||
---
|
||||
|
||||
Приложение работает на **встроенном `node:sqlite`** (`const { DatabaseSync } = require('node:sqlite')`), а **не** на `better-sqlite3` — последний в дереве не установлен (require падает MODULE_NOT_FOUND). Это исправляет устаревшую запись «better-sqlite3» в [[project_status.md]] и индексе MEMORY.md.
|
||||
|
||||
- Подключение: `backend/src/db/db.js` (`new DatabaseSync(dbPath)`).
|
||||
- Конфиг пути: `backend/src/config.js` → `DB_PATH` (по умолчанию `backend/data/learnspace.db`).
|
||||
- Живая БД: **`backend/data/learnspace.db`** (~5.2 МБ). В дереве есть и другие копии (`data/learnspace.db`, `backend/src/data/...`, `backend/src/db/data/...`) — это НЕ боевая.
|
||||
- Node 24 → `node:sqlite` доступен (экспериментальный, кидает ExperimentalWarning в stderr).
|
||||
|
||||
API node:sqlite: `db.prepare(sql).all()/.get()/.run()`; для readonly — `new DatabaseSync(path, { readOnly: true })`.
|
||||
|
||||
Замечание по окружению: в Bash-туле кириллический путь `Тесты` иногда искажается (`Тесты`→`"5ABK`), из-за чего node-скрипты падают на ENOENT/MODULE_NOT_FOUND. PowerShell путь не ломает.
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: reference_student_materials
|
||||
description: "«Мои материалы» — ученик сохраняет к себе материалы онлайн-урока (доска/заметка), копия переживает удаление сессии"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: 60467058-b40e-4bd9-9f7f-d1e362e8039a
|
||||
---
|
||||
|
||||
Личная коллекция ученика «Мои материалы» (сделано 2026-06-04, commit 44ab5e0). Контекст:
|
||||
живой урок (классрум) уже персистит доску/чат/заметки, и ученик их видит на `my-lessons.html`
|
||||
(только ученик; учителя редиректит на `/lesson-history`). Но всё привязано к сессии — учитель
|
||||
может `DELETE /api/classroom/:id/history` и стереть. Решение — независимая копия у ученика.
|
||||
|
||||
- Миграция **060**: `student_materials(user_id, kind CHECK board|note|link|image, title, body,
|
||||
url, source_session_id FK→classroom_sessions ON DELETE SET NULL, source_title denormalized, created_at)`.
|
||||
- API **/api/materials** (`routes/materials.js` + `studentMaterialsController.js`): GET list (свои),
|
||||
POST create (валидация kind/обязательных полей), DELETE /:id (проверка владельца). Любой
|
||||
авторизованный (в осн. ученики). Хелперы `LS.listMaterials/saveMaterial/deleteMaterial` в js/api.js.
|
||||
- Доска: добавлен `Whiteboard.exportBlob(cb)` (как exportPNG, но отдаёт Blob). Кнопка «К себе» на
|
||||
доске → exportBlob → `LS.uploadFile` (/api/files) → saveMaterial(kind:'board', url). Кнопка «К себе»
|
||||
на заметке → saveMaterial(kind:'note', body). Обе в `my-lessons.html` (страница ученика).
|
||||
- Новая страница **/my-materials** (`frontend/my-materials.html`): сетка карточек (доска=картинка
|
||||
открыть/скачать, заметка=текст, ссылка), удаление. Пункт сайдбара «Мои материалы» (только ученик,
|
||||
js/sidebar.js, группа «Учебный процесс»).
|
||||
- **Сохранение ЧАСТИ доски** (commit 116876d→fcb8ef7): логика вынесена в общий **`/js/board-clip.js`**
|
||||
(`BoardClip.savePage(wb,meta,btn)` / `saveRegion(...)` + кроп-оверлей; meta={sourceSessionId,sourceTitle,pageNum}).
|
||||
Кроп: exportBlob снимка → выделение прямоугольника → обрезка (offscreen canvas, коорд × naturalW/displayedW)
|
||||
→ /api/files → saveMaterial(kind:'image'). Подключён И в **my-lessons.html** (просмотр), И в **classroom.html**
|
||||
(ЖИВОЙ урок): кнопки «Область»/«К себе» в ученической панели `#cr-student-nav`, обёртки
|
||||
crSaveBoardPage/crSaveBoardRegion над `_wb`+`_session`. Инстанс живой доски в классруме — `_wb`.
|
||||
**План развития:** `plans/my-materials/PLAN.md` (6 фаз). Сделано:
|
||||
- **Ф1** (fd3e5c4): `PATCH /api/materials/:id` (title/body), кнопка «+Заметка» (личный блокнот), «Изменить» на карточках.
|
||||
- **Ф2** (2c7e974): миграция **061** `material_collections`(папки) + `student_materials.collection_id`(ON DELETE SET NULL)+`tags`;
|
||||
CRUD коллекций `/api/materials/collections`; GET /materials отдаёт {materials, collections}; на странице — бар папок,
|
||||
поиск, фильтр по типу, перенос в папку (select на карточке).
|
||||
- **Ф6a** (9c95dc8): кнопки «К себе»/«Область» учителю в `lesson-history.html`; пункт сайдбара «Мои материалы» виден всем.
|
||||
- **Ф3** (61e30be+43fe90d): `js/material-save.js` (MaterialSave.note/link/image); кнопка «В мои материалы»
|
||||
на задачах экзамена (task-card.js, заметка=условие+ответ+решение); на учебнике — `js/textbook-clip.js`
|
||||
(плавающая кнопка, сохраняет § ссылкой), инжектится сервером в /textbook/:slug рядом с deep-link.
|
||||
- **Ф4** (d3a64ac): svg-draw `opts.bgImage` + `exportFlatBlob()` (растер подложка+вектор→PNG); на странице —
|
||||
«Рисунок» (с нуля) и «Аннотировать» (поверх board/image) через модалку SvgDraw.
|
||||
- **Ф5** (e793b4e): «В флешкарты» на заметке → выбор/создание колоды → карточка (front=заголовок, back=текст);
|
||||
хелперы fcListDecks/fcCreateDeck/fcAddCard.
|
||||
- **Ф6b** (f7357ad): `POST /api/materials/:id/share {classId|userId}` (teacher/admin) — независимая КОПИЯ
|
||||
каждому ученику (source_title «Раздатка: <учитель>») + SSE-уведомление; кнопка «Раздать» (учителю).
|
||||
- ПЛАН ЗАВЕРШЁН (все 6 фаз). Не делалось из обсуждения: теги-UI (поле tags есть, UI нет), экспорт PDF/ZIP,
|
||||
«учить из материалов», SRS-интеграция.
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: reference_svg_drawer
|
||||
description: "Лёгкий векторный SVG-редактор (рисовалка) для уроков — API виджета, санитайзер, точки переиспользования"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: 60467058-b40e-4bd9-9f7f-d1e362e8039a
|
||||
---
|
||||
|
||||
SVG-рисовалка в редакторе уроков. Сделано 2026-06-03 (commit ef59023).
|
||||
|
||||
**Виджет:** `frontend/js/svg-draw.js` → `window.SvgDraw.mount(container, {svg, width, height, onChange})`
|
||||
→ `{getSVG(), destroy(), el}`. Vanilla, рендер в SVG-DOM (НЕ canvas, в отличие от whiteboard.js).
|
||||
Инструменты: перо (Catmull-Rom→bezier), линия, прямоугольник, эллипс, стрелка, текст,
|
||||
цвет/толщина/заливка, выбор (перемещение+Delete), undo/redo, очистка. Стили инжектятся сами.
|
||||
Координаты через `svg.getScreenCTM().inverse()`. viewBox по умолчанию 800×500.
|
||||
|
||||
**Санитайзер:** `frontend/js/svg-sanitize.js` — UMD (`window.SvgSanitize` + `module.exports`),
|
||||
`clean(str)`. Браузер → DOM-whitelist; node → консервативный regex. Whitelist тегов
|
||||
(svg,g,path,line,rect,circle,ellipse,polyline,polygon,text,tspan,defs,*Gradient,stop) и
|
||||
геометрия/стиль-атрибутов; режет script/foreignObject/style/image/a/use, on*=, href/xlink:href,
|
||||
javascript:. БЕЗ зависимостей. Бэкенд `lessonController.js` подключает его кросс-путём
|
||||
`require('../../../frontend/js/svg-sanitize.js')` — единый источник правды.
|
||||
|
||||
**Блок урока `svg-draw`** (хранение inline, переоткрывается для дорисовки):
|
||||
- `lessonController.js`: `svg-draw` в VALID_TYPES + `cleanSvg(data.svg)` при сохранении (defense-in-depth).
|
||||
- `lesson-editor.html`: палитра «Рисунок», BLOCK_DEFAULTS `{svg:'',caption:''}`, `renderBlockEditor`
|
||||
case (host `.svgdraw-host[data-bid]` + подпись), `mountSvgDrawEditors()` (монтаж/перемонтаж в
|
||||
renderBlocks, инстансы в `_svgDrawInst`), `renderPreviewBlock` case (санитизированный inline-svg).
|
||||
- `lesson.html`: `renderBlock` case svg-draw (санитизированный inline-svg, адаптивно `max-width:100%`).
|
||||
|
||||
**Переиспользование (заявлено):** тот же `SvgDraw`/`SvgSanitize` пригодны для картинок флешкарт и
|
||||
**фигур генератора задач** (см. [[reference_exam_textbook_links]] / обсуждение параметрического генератора).
|
||||
|
||||
**MVP-ограничения (на доработку):** select только move+delete (нет resize/rotate); нет слоёв/
|
||||
группировки, привязки к сетке, импорта картинки-подложки; текст однострочный; один размер холста.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: reference_textbook_latex_escaping
|
||||
description: "Баг формул в учебниках = ЛИШНИЕ слэши (over-escaping), не нехватка; правило чётности и фикс-скрипт"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: a705e035-e600-43a2-b98c-197923986186
|
||||
---
|
||||
|
||||
Формулы в учебниках (`frontend/textbooks/*.html`) лежат в JS-строковых литералах, рендерит KaTeX через `renderMathInElement`. Симптом «формула печатается текстом» (`dfrac13S_осн`, `sqrtR^2+h^2`, `cdoth` — карточка пирамиды/конуса в geometry_11_ch2) — это **ЛИШНИЕ обратные слэши (over-escaping)**, а НЕ их нехватка. (Первичная гипотеза «не хватает \» была НЕВЕРНА — проверять байты ДО выводов.)
|
||||
|
||||
Механика: в литерале `\\\\dfrac` (4 слэша) вместо `\\dfrac` (2). После JS-анескейпа KaTeX получает `\\dfrac` → трактует `\\` как перенос строки, а `dfrac` печатает как обычный текст, поэтому формула разваливается на строки.
|
||||
|
||||
**Правило чётности (защищает легитимные `\\` разделители строк):**
|
||||
- 2 слэша → `\cmd` → ОК
|
||||
- 4 слэша → `\\`+текст → БАГ → схлопнуть до 2
|
||||
- 6 слэшей → `\\`+`\cmd` (перенос строки + команда в `\begin{cases}`) → ОК, не трогать
|
||||
- 8 слэшей → БАГ → до 2
|
||||
Схлопывать ТОЛЬКО прогоны слэшей, кратные 4, и ТОЛЬКО перед известной LaTeX-командой. Перед `x`/цифрой (настоящие `\\` в cases/array) — не трогать.
|
||||
|
||||
**Исправлено 2026-05-30:** 150 правок, 7 файлов (`algebra_11_ch1/ch2/ch3`, `geometry_11_ch1/ch2/ch3/ch4`), коммит 8786cf5 (запушен в master). Скрипт: `backend/scripts/fix_overescaped_latex.js` (идемпотентный, dry-run по умолчанию, `--apply`, с KaTeX-валидацией). algebra_8 / algebra_7_ch4 имели только легитимные `\begin{cases}` → 0 правок.
|
||||
|
||||
**БД чиста:** questions колонки `text/explanation/correct_text` (НЕ `payload`!), 1398 вопросов + 5187 options (`options.text`) → 0 багов обеих форм. Баг только в HTML.
|
||||
|
||||
Окружение этой сессии: stdout периодически рвался, Read иногда галлюцинировал/дублировал содержимое — надёжно писать результат в файл с маркерами `<<<BEGIN>>>/<<<END>>>` и читать через PowerShell `[IO.File]::ReadAllText`. Bash искажает кириллический путь `Тесты`→`"5ABK` (ENOENT) — использовать PowerShell. БД — через `node:sqlite` (см. [[reference_sqlite_node]]).
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: reference-textbook-sources
|
||||
description: "Расположение PDF-источников белорусских учебников (физика, алгебра, геометрия 7-11) для опоры на оглавление и дидактическую структуру при разработке HTML-учебников LearnSpace"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: 3d2e8bf3-6bcb-469f-b8c1-c4e7513b3b56
|
||||
---
|
||||
|
||||
# Папка с учебниками
|
||||
|
||||
**Корень:** `G:\Dev\Тесты\Методички\тест_6 класс\Книги\`
|
||||
|
||||
## Физика
|
||||
| Класс | Файл | Авторы | Изд. |
|
||||
|---|---|---|---|
|
||||
| 7 | `fizika_Isachenkova_7kl_rus_2022.pdf` | Исаченкова Л.А. | 2022 |
|
||||
| 8 | `fizika_8kl_isachenkova_rus_2018.pdf` | Исаченкова Л.А. | 2018 |
|
||||
| 9 | `Fizika_Isachenkova_9_rus_2019.pdf` | Исаченкова Л.А., Сокольский А.А., Захаревич Е.В. (под ред. Сокольского) | Народная асвета, 2019, ISBN 978-985-03-3082-6 |
|
||||
| 10 | `fizika_10kl_gromika_rus_2019.pdf` | Громыко | 2019 |
|
||||
| 11 | `fizika_11kl_zhilko_rus_2021.pdf` | Жилко, Маркович, Сокольский | 2021 |
|
||||
|
||||
## Алгебра/Геометрия 9
|
||||
Указаны в [[plans/textbooks-9/PLAN.md]] — Арефьева И.Г. (Алгебра 2019), Казаков В.В. (Геометрия 2019), та же папка `Книги/`.
|
||||
|
||||
## Структура параграфа в учебниках Исаченковой
|
||||
Каждый § оформлен по канве:
|
||||
1. Текст с жирными определениями и формулами
|
||||
2. Рисунки/фото
|
||||
3. «Главные выводы» (оранжевый блок)
|
||||
4. «Контрольные вопросы» (розовый)
|
||||
5. «Домашнее задание» (синий)
|
||||
6. «Упражнение N» (нумерованные задачи, часть с 🦉 — повышенный уровень)
|
||||
7. «Для любознательных» (опциональный расширенный блок)
|
||||
8. Иконки 📱 (видео-опыт) и 🎱 (интерактивная модель в ЭОР)
|
||||
|
||||
При написании HTML-учебников использовать эту канву как дидактический шаблон. Иконки → inline SVG `.ic` (см. [[feedback_no_emoji]]).
|
||||
|
||||
## Содержание Физики 9 (Исаченкова 2019)
|
||||
- **Глава 1 «Основы кинематики»** — §1-14
|
||||
- **Глава 2 «Основы динамики»** — §15-24
|
||||
- **Глава 3 «Основы статики»** — §25-30
|
||||
- **Глава 4 «Законы сохранения»** — §31-36
|
||||
- **Глава 5 «Лабораторный эксперимент»** — ЛР №1-12 (стр. 180-198)
|
||||
- Приложение 1 — лабораторное оборудование (стр. 200)
|
||||
- Приложение 2 — видео к иллюстрациям (стр. 204)
|
||||
|
||||
§22 (движение под углом к горизонту) и §30 (плавание судов, воздухоплавание) помечены «для дополнительного чтения».
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: reference_vex_search
|
||||
description: "vex (code-search CLI) установлен и проиндексирован; правило когда vex, когда ast-index"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: reference
|
||||
originSessionId: a02c76bd-13fd-4ebe-b133-375f6c469212
|
||||
---
|
||||
|
||||
vex v1.11.0 — гибридный поиск по коду (vector+index), установлен в `C:\Users\Home\bin\vex.exe`
|
||||
(в пользовательском PATH; в новых терминалах — просто `vex`, в уже открытых сессиях PATH не подхвачен — звать по полному пути). Проект BQ-System проиндексирован: структурный + **semantic** (16360 символов, embeddings enabled).
|
||||
|
||||
**Когда что** (подробно — `.claude/rules/search-tools.md`, закоммичено f2b0db4):
|
||||
- **ast-index** — дефолт: символ по имени, **usages/callers**, outline. usages/callers по JS — ТОЛЬКО ast-index (vex их пропускает: чистый JS не binder-язык; `vex usages "audit"` → пусто, `ast-index` → все 10).
|
||||
- **vex** — `vex search "..." --semantic`, `vex similar "X"` (по смыслу), `vex pattern --lang js '...'` (AST), `vex duplicates`, `vex show "X"` (компактное тело).
|
||||
- Grep всё ещё запрещён (см. [[reference_sqlite_node]]).
|
||||
|
||||
**Гочи:**
|
||||
- Модель MiniLM (~86 МБ) при прерванном скачивании бьётся → `failed to load ... Protobuf parsing failed`. Фикс: `Remove-Item C:\Users\Home\AppData\Local\vex\embeddings -Recurse -Force`, затем `vex index --semantic`. Качать в форграунде (фоновый процесс прервался на середине).
|
||||
- После коммитов HEAD сдвигается → vex пишет "index may be stale" → `vex update` (инкрементально, semantic сохраняется из манифеста).
|
||||
- `search`/`usages`/`show` берут индекс текущей папки и НЕ принимают `--path`; `pattern` требует `--lang`+`--path`.
|
||||
- settings.json: правило `"Bash(vex:*)"` пользователь добавляет САМ — Claude не может сам себе выдавать права (классификатор блокирует self-modification).
|
||||
Reference in New Issue
Block a user