Главная причина почему «Существует или нет?» (§1) не работал:
В buildP1 setTimeout цепочка была:
initRing() → initCalc() → initSquares() → initExists() → initDual()
initSquares() — функция-не-существует (игра запускается по кнопке через squaresStart). ReferenceError рушил цепочку, поэтому initExists() и initDual() НЕ ВЫЗЫВАЛИСЬ → у dropzones не было event-listeners для drag/click → drag-and-drop не работал.
Та же проблема была в §2 с initRationality() — функция отсутствует, riStart() запускает игру по клику.
Исправил обе цепочки.
В dragRender() (Drag 'упрости √') был while-цикл, который требовал 5 уникальных значений из набора [4,9,16,25,36,49,64,81]. Логика:
- если делит t.n нацело → всегда добавляем
- иначе → добавляем только если size<3
Для t.n=50: единственный делитель из набора это 25. После добавления sq+2 произвольных (size=3), цикл требует только делители — других нет → бесконечный цикл → зависание.
Аналогично ломалось на: 200, 48 и др.
Фикс:
1) сначала добавляем ВСЕ делители-квадраты из расширенного набора (100, 121 включены)
2) затем добивает случайными до 5 штук с лимитом 30 итераций (страховка)
3) берётся slice(0,5) на случай если ВСЕ 10 кандидатов делят t.n
После Wave 3 поле #search-inp в шапке было заменено на модальный поиск Ctrl+K с #search-modal-input. Но старая функция initSearch() в init() продолжала вызывать getElementById('search-inp').addEventListener(...) — что бросало TypeError на null и крашило init() до построения первого параграфа (отсюда подвисание страницы при загрузке).
Фикс: добавлен guard 'if(!inp) return;' — функция остаётся для обратной совместимости (на случай восстановления старого input).
1. XP/уровни: XP_LEVELS[11], addXp(source) во всех тренажёрах и квизах, синий level-up popup, XP-карточка в сайдбаре. Persists в LocalStorage algebra8_ch1_xp
2. Streak-серии: текущая+рекорд, milestones 3/5/7/10 → оранжевый popup + ачивки streak3/5/7/10. Сброс на ошибке
3. Daily Challenge: 7 задач в DAILY_TASKS, дата-гарда, кнопка в шапке с пульсирующим индикатором, модалка с вопросом, +30 XP за прохождение
4. Achievements Gallery: кнопка 'Трофеи' в шапке, модалка с сеткой 20 ачивок (ACH_DEFS), SVG-иконки, статус earned/locked
5. Circular Progress: SVG-кольцо вместо линейной полосы на карточках §§ в para-selector
6. Финальный фейерверк: при общем прогрессе ≥95% автомодалка с confetti×5, статистикой XP/streak/achievements, освоенными темами
7. Sound effects: playTone() через Web Audio, sounds.correct/wrong/levelUp/achievement, кнопка mute в шапке с LocalStorage флагом
Все существующие функции (BUILDERS, STATE.progress, achievement, goTo, buildPN) — без изменений, новое добавлено через IIFE-обёртки.
1. Ctrl+K поиск: модалка со списком, индексирует параграфы, виджеты, карточки, термины глоссария. Стрелками выбор, Enter переход
2. Клавишные шорткаты: 1-7 → §§, ←/→ навигация, Esc закрыть модалки, ? показать справку. Игнорируется при фокусе в input
3. Закладки: SVG-кнопка в углу каждой .card (filled/outlined), хранятся в LocalStorage algebra8_bookmarks. В сайдбаре раздел 'Мои закладки' с переходом и удалением
4. Глоссарий-tooltips: 13 терминов (арифметический корень, радикал, иррациональное, модуль, промежуток, интервал, отрезок, система, совокупность, двойное неравенство и др.). DOM-walker оборачивает термины в .gloss с подчёркиванием, hover показывает определение в floating-tooltip
5. Mini-map: фиксированная панель справа с точкой на каждый .card/.wg в активной секции, активная подсвечивается по скроллу, скрывается на ≤980px
6. 3-уровневая подсказка: 'Подсказка' рядом с 'Проверить' в simp4 и compare. Уровень 1: намёк, 2: шаг, 3: полный ответ (−5 очков)
7. Шпаргалка drawer на мобильном: hamburger-кнопка в шапке, sidebar выезжает справа на ≤980px (transform translateX)
1. Боксёрский ринг (§1): SVG-канаты вокруг квадрата + 4 цветные угловые подушки + ковёр-pattern + bell-звук через Web Audio API при S=36 + анимация боксёра-победителя на 2с
2. Доказательство √(ab)=√a·√b (§3): кнопка 'Воспроизвести' запускает 5-шаговую анимацию (подсветка прямоугольника → разрез на единичные клетки → склейка в квадрат → бейдж 'Доказано!')
3. Drag&drop с инерцией (§4): pointer-based DnD с ghost-карточкой следующей за курсором, drop-zone подсветка, неверный → тряска и возврат с инерцией, кнопочный fallback для тача
4. Match-игра (§3): SVG-overlay рисует линии соединения между парами выражений (синяя dashed pending → зелёная при совпадении / красная мигающая при ошибке)
5. Real-time валидация (liveCheck): ✓/✗ индикатор появляется при вводе во всех числовых input'ах без нажатия 'Проверить'
6. Game-over modal (squares): красивая модалка с рекордом, SVG-кубком, confetti
7. Hover-preview карточек §§: tooltip с темами параграфа и прогресс-баром
8. Fade-переходы между секциями: 180ms fadeOut + 220ms fadeIn с translateY
1. Финал главы:
- После buildAssessment() повторный renderMath(body) — захватывает формулы из квиза
- Дополнительный renderMath через 300ms — на случай если KaTeX не успел загрузиться
2. Мета-теги Cache-Control no-cache, no-store, must-revalidate / Pragma no-cache / Expires 0 — чтобы прежняя версия страницы не зависала в кэше браузера (поэтому и шапка не обновлялась)
Было: init() синхронно вызывал buildP1...buildFinal — 7 секций × ~500 строк HTML, плюс KaTeX renderMathInElement сканировал весь body. На медленном CPU могло подвисать на 2-5 секунд.
Стало: init() строит только §1 (через goTo('p1')). Остальные секции строятся лениво при первом goTo(id) — кэшируются в BUILT Set.
Профит: первая отрисовка в 7 раз быстрее. KaTeX-рендер тоже только для активной секции.
Прошлый коммит хранил название в Map, но старые записи в LocalStorage (Set из id-ов) подгружались с id в качестве текста — пользователь по-прежнему видел 'ring36', 'start'.
Фикс: словарь ACH_LABELS (id → название) применяется при загрузке как fallback:
- старый формат массив id-ов: id → ACH_LABELS[id]
- новый формат объект {id:text}: если text === id, используем ACH_LABELS[id]
Теперь при следующем открытии учебника старые достижения автоматически получат красивые названия.
Было: 'ring36', 'start' — внутренние id-ы достижений
Стало: 'Начало пути по корням!', 'Нашёл сторону ринга'
STATE.achievements теперь Map(id → text). Старый формат массива id-ов читается с обратной совместимостью (id используется как текст). При сохранении пишется как объект.
Убраны:
- <section class="intro"> с заголовком 'Изучаем 3 раздела физики' (там всё ещё видна была старая надпись 'Автор: Исаченкова Л. А.')
- <section class="info-grid"> с 4 info-карточками (Интерактив в каждом §, Прогресс сохраняется, Формулы — KaTeX, Светлая и тёмная тема)
Hub теперь чище: шапка → общий прогресс → 3 карточки разделов → подвал.
- Миграция 011: UPDATE textbooks SET author='' (все 4 записи)
- algebra_8.html: убрано из <title> и футера
- physics_8.html (hub): убрано из title/header/intro/footer, заменено на LearnSpace
- physics8_*.html (3 файла): убраны все вхождения '· Исаченкова' в подписях §
- physics_9.html: убраны все вхождения '· Исаченкова' в подписях §
- chemistry_9.html: убраны 3 упоминания '· Шиманович' в подписях
В каталоге /textbooks автор больше не отображается под названием (так как поле пустое).
Подход: hub-страница, а не слияние файлов.
Проблема: 3 готовых файла-главы (thermal/electro/optics) занимали 3 карточки в каталоге. Физическое слияние в один файл = 800КБ+, конфликты CSS/JS namespaces, риск сломать KaTeX.
Решение:
- Создан frontend/textbooks/physics_8.html — hub-страница с 3 крупными карточками-разделами (амбер/синий/фиолетовый)
- Карточки ссылаются напрямую на /textbooks/physics8_thermal.html и т.д. (express.static уже отдаёт эти файлы)
- Из каталога /textbooks теперь видна ОДНА карточка «Физика 8», sort_order 4
- Hub-страница показывает прогресс по каждой главе через LocalStorage (best-effort парсинг)
- Header «К каталогу», переключатель темы синхронизирован с главами
Миграция 010: удалила 3 прежние записи (physics-8-thermal/electro/optics), добавила физическо-8 → physics_8.html, para_count=40.
Эмодзи в hub не используются (только inline SVG). Эмодзи в файлах глав остались — это контент.
Интегрирован готовый интерактивный учебник по физике 8 класса (40 параграфов, разбитых на 3 файла):
- physics8_thermal.html (§1–11) — Тепловые явления
- physics8_electro.html (§12–31) — Электрические явления
- physics8_optics.html (§32–40) — Световые явления
Все три самодостаточные (KaTeX через CDN, шрифт Outfit, dark mode, анимации, эмодзи).
Автор: Исаченкова Л. А.
Миграция 009 регистрирует три новых textbook-записи:
- physics-8-thermal (amber, sort 4)
- physics-8-electro (blue, sort 5)
- physics-8-optics (violet, sort 6)
После миграции доступны через /textbook/physics-8-thermal и т.д. и видны в каталоге /textbooks.
Pre-commit hook на эмодзи обойден --no-verify по разрешению пользователя: эмодзи здесь являются частью авторского контента учебника (визуальные маркеры разделов: тепловые/электрические/оптические явления), а не нашим кодом.
Боковая шпаргалка строилась обычным HTML (Unicode-символы √ ≤ ⊂), формулы не оформлялись как настоящие математические.
Фикс:
- Все формулы в SIDEBARS обёрнуты $-делимитерами KaTeX (\sqrt, \mathbb, \cap, \subset, \Leftrightarrow и т.д.)
- После buildSidebar() вызывается renderMathInElement(box) для встроенного рендера
- Учебник теперь показывает корни и множества в правильной типографике
Фаза 1 — структура и каталог:
- frontend/textbooks/chemistry_9.html (Шиманович, 60 §) + physics_9.html (Исаченкова, 38 §)
- frontend/textbooks.html — каталог в стиле LearnSpace (карточки с обложками)
- Маршруты: /textbooks (каталог), /textbook/<slug> (полноэкранный учебник)
- Сайдбар: пункт «Учебники» (book-open-text)
- Feature flag feature_textbooks_enabled, hideDisabledFeatures map
Фаза 2 — прогресс в localStorage + UI чтения:
- frontend/js/textbook-tracker.js — инжектится в каждый учебник:
- «← Учебники» overlay-кнопка (top-left, semi-transparent)
- «Прочитано» чекбокс рядом с каждым §-заголовком
- Зелёный dot на pill уже прочитанных параграфов
- Авто-открытие последнего параграфа при возврате
- Каталог показывает прогресс-бар «X из Y прочитано» + кнопку «Продолжить»
Фаза 3 — серверный прогресс + назначение чтения как ДЗ:
- Таблица textbooks (slug, subject, grade, title, author, color, ...)
- Таблица textbook_progress (user_id, textbook_id, JSON read[], last_para)
- Колонки assignments.textbook_id + textbook_paragraphs
- API: GET /api/textbooks (с прогрессом), GET /:slug, POST /:slug/progress,
GET /:slug/class-progress (учитель)
- tracker.js синхронизирует прогресс через POST /progress (если залогинен)
- На каталоге у учителей кнопка «Назначить чтение» — модалка с выбором
классов + параграфы («1-5» или «1,3,5») + deadline
- bulkCreateAssignment расширен: принимает textbook_slug, резолвит в id
Миграция 004 идемпотентная; сиды двух учебников включены.