Commit Graph

33 Commits

Author SHA1 Message Date
Maxim Dolgolyov 660e7e2747 feat(gamification): Phase 1 — full kill-switch + textbook XP wrapping
Until now the 'gamification' feature flag did nothing: it had no row in
app_settings, the admin couldn't toggle it, awardXP/awardCoins ignored
it, and the CSS only hid three dashboard widgets — XP bars in textbooks
stayed visible regardless.

Phase 1 closes every hole.

Backend (source of truth):
  • migration 029 seeds feature_gamification_enabled=1
  • new isGamificationEnabled() helper in gamification/_shared.js with a
    30s cache + invalidateGamificationCache() for instant admin toggles
  • awardXP / awardCoins / updateStreak / unlockAchievement /
    checkAchievements all bail out when the flag is off
  • /api/gamification/* and /api/shop/* (user routes) return 404 when
    disabled; admin routes remain open so the switch itself is reachable
  • adminController.updateFeatures gains 'gamification' in the allow-list
    and invalidates the cache on flip

Frontend:
  • LS.isGamificationEnabled() (synchronous, populated by loadFeatures)
    so xp.js + applyCosmetics can bail without a round-trip
  • xp.js load/add/flush become no-ops when the flag is off
  • applyCosmetics skips the round-trip when off
  • CSS .no-gamification rule expanded to cover .hero-xp-badge, .po-xp,
    .xp-card, .xp-bar, #frames-section, and a universal [data-gamified]
    hook for future blocks

Textbooks (Variant 2 of the plan):
  • backend/scripts/wrap_textbook_xp.py — idempotent script that adds
    data-gamified to 167 XP tags across 63 textbook files (chapters +
    hubs, all subjects/grades). Single CSS rule now hides everything.

Verified end-to-end: with the flag off, awardXP/awardCoins write nothing;
flipping back restores normal behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 19:43:24 +03:00
Maxim Dolgolyov dad34dc1d6 fix(algebra-8 ch1): прогресс пишется под правильный slug + миграция 016
После переименования slug algebra-8 → algebra-8-ch1 (миграция 014) Глава 1
продолжала POSTить прогресс под старым именем 'algebra-8', который теперь
указывает на hub-строку. Эффект: paragraphs_read и last_para уходили в
hub-row, а каталог хабов их игнорировал (агрегирует только children).

Фикс:
- algebra_8.html: _TB_SLUG = 'algebra-8-ch1'
- migration 016: union перенос ошибочно записанного прогресса из hub в
  ch1; очистка hub-row. Идемпотентно (NOT EXISTS guard).

Проверено: после миграции у user 2 paragraphs_read='["p1"]' живёт в
ch1-row, hub-row пуста.

Другие учебники проверены — корректно:
- ch2/ch3 уже использовали правильные slug
- chemistry-9, physics-9, physics8_* подключены через textbook-tracker
- algebra_8_hub.html и physics_8.html — хабы без tracker (правильно)
2026-05-27 17:03:59 +03:00
Maxim Dolgolyov 699fdcc7fb feat(catalog): хаб-страница для Алгебры 8 (3 главы под единым слагом)
- migration 014: parent_slug column + algebra-8 hub row +
  rename old algebra-8 → algebra-8-ch1 (progress сохраняется
  через стабильный textbook_id=3)
- backend/routes/textbooks.js: GET / фильтрует parent_slug IS NULL;
  aggregated progress для хабов; новый GET /:slug/children
- algebra_8_hub.html: новая хаб-страница с 3 карточками глав,
  hero с общим прогрессом, XP-бейдж, ссылки на главы
- algebra_8/ch2/ch3: кнопки cross-chapter заменены на
  одну «К алгебре 8» в шапке

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 16:49:20 +03:00
Maxim Dolgolyov c2ef4f4898 feat(algebra-8 ch3): Wave 4 — финал, 7 боссов, практика
§ Финал главы 3:
- BOSS ARENA: 7 боссов (5–7 заданий каждый, иконки <>, ±, [], ∩, ∪, 1/x, ★):
  · §13 Хранитель сравнения — свойства, транзитивность, смена знака
  · §14 Алхимик границ — оценки x+y, x-y, xy
  · §15 Архитектор промежутков — линейные неравенства, запись
  · §16 Дирижёр пересечений — системы и совокупности
  · §17 Мастер параболы — квадратные, D, корни
  · §18 Властелин ОДЗ — дробно-рац., выколотые точки
  · ★ Чемпион неравенств (финал) — 7 заданий из всей главы
- Универсальный движок select / yesno / input, HP-бар, состояние в localStorage
- Сертификат «Чемпион неравенств» при всех 7 победах

Увлекательная математика (3 факта):
- Почему меняется знак при умножении на отрицательное
- Кто придумал знаки $<$ и $>$ (Хэрриот, 1631)
- Неравенство Коши (a+b)/2 ≥ √(ab)

Финальная практика — генератор 5 типов задач (линейные, оценка,
системы, квадратные, ОДЗ). Серия из 5 = достижение.

algebra_8.html: добавлена ссылка «Глава 3 →» в шапке.
2026-05-27 16:24:12 +03:00
Maxim Dolgolyov 66166f6294 feat(algebra-8): синхронизация прогресса учебника с каталогом
Раньше: алгебра 1 и 2 главы хранили прогресс только в localStorage,
поэтому каталог /textbooks показывал 0/N прочитано и кнопку 'Открыть'
даже после активной работы с учебником.

Теперь обе главы шлют POST /api/textbooks/:slug/progress:
- markLastPara(id) — при каждом goTo(); сервер запоминает last_para,
  каталог показывает кнопку 'Продолжить'.
- markParaRead(id) — когда STATE.progress[key] первый раз ≥ 50%
  (внутрипараграфный прогресс достаточен); сервер добавляет id в
  paragraphs_read[], каталог показывает '1/7 прочитано'.
- Дебаунс 600мс — несколько быстрых переходов схлопываются в один POST.
- keepalive:true + beforeunload-flush, чтобы последний переход не
  потерялся при закрытии вкладки.
- loadServerReadState() при init() — если на другом устройстве уже
  прочитаны параграфы, локальный STATE.progress поднимается до 100%
  для них (визуально совпадает с каталогом).

Slug: 'algebra-8' для ch1, 'algebra-8-ch2' для ch2.
2026-05-27 16:01:26 +03:00
Maxim Dolgolyov 64bd44088d feat(xp): textbook XP синхронизируется с системной геймификацией
- backend: POST /api/gamification/self-award (rate-limited, validated)
- frontend/js/xp.js: load/add/flush/on клиент, ~150 LOC, дебаунс 300мс,
  keepalive fetch на unload/visibilitychange hidden
- algebra_8.html и algebra_8_ch2.html: XP_LEVELS заменён на единую
  формулу с сервером; addXp/loadProgress подключены к window.LS.xp
- При первой загрузке: merge max(local, server); далее сервер — источник
  правды
2026-05-27 15:56:36 +03:00
Maxim Dolgolyov 9199427dfd feat(algebra-8): общая система опыта для главы 1 и главы 2
Раньше: каждая глава хранила XP отдельно (algebra8_ch1_xp +
algebra8_ch2_xp), формулы уровня были разные (дискретная таблица в
ch1, формула sqrt в ch2), визуально XP-карты различались.

Теперь:
- Один ключ localStorage: 'algebra8_xp' для обеих глав.
- При первой загрузке (в любой главе) — single-shot миграция:
  если новый ключ отсутствует, суммирует старые ch1 + ch2 и
  сохраняет под единый ключ. Старые ключи не удаляются (на всякий).
- Единая таблица уровней XP_LEVELS = [0, 50, 120, 220, 350, 520,
  740, 1000, 1300, 1700, 2200] (11 уровней, MAX = Ур. 11).
- Единые функции calcLevel(xp) и _xpForLevel(lv).
- XP-карта в сайдбаре главы 2 теперь идентична главе 1:
  градиент acc→pri-soft, .xp-card-title, .xp-bar, .xp-fill, .xp-nums.
- Hero badge «★ Ур. N · NN XP» добавлен в hero обоих глав.
- addXp в ch2: при повышении уровня — popup с номером уровня + confetti.
- addXp в ch1: refreshProgressUI вызывается, чтобы обновлять hero
  badge сразу после начисления.
2026-05-27 15:41:54 +03:00
Maxim Dolgolyov c2f66b1e97 feat(algebra-8 ch2): Wave 4 — финал + 7 боссов + DB migration
frontend/textbooks/algebra_8_ch2.html · final2:
- Boss Arena с 7 боссами:
  · §7 «Хранитель неполных» — 5 заданий
  · §8 «Дискриминатор» — 5 заданий
  · §9 «Дух Виета» — 5 заданий
  · §10 «Разложитель» — 5 заданий
  · §11 «Архивариус задач» — 5 заданий
  · §12 «Мастер замены» — 5 заданий
  · ★ «Магистр алгебры» (финал) — 7 заданий
- Универсальный движок: select / yesno / input
- HP-бар, иконки боссов, состояние в localStorage
- Сертификат «Магистр квадратных уравнений» при всех 7 победах

Дополнительно:
- Увлекательная математика (3 spoiler-факта: история, a≠0, парадокс)
- Финальная практика: генератор случайных задач со всей главы (5 типов)
- Серия из 5 верных = достижение
- ACH_LABELS для всех boss_*, all_bosses, prac_streak

algebra_8.html: добавлена ссылка «Глава 2 →» в шапке.
migrations/012_algebra_8_ch2.sql: регистрация slug 'algebra-8-ch2'.
2026-05-27 14:54:01 +03:00
Maxim Dolgolyov 31fb5d7ab0 feat(textbooks): Wave Bosses — 7 битв-проверок (+971 строка)
В конце каждого § перед secNav добавлена карточка 'Босс §N: <тема>' с битвой из 5-7 задач.

7 битв:
- §1 «Знаток корней» (7 задач): √121, √50 vs 7, √(−9), (√5)², √(a²), √0.81, число корней из 100
- §2 «Эксперт по числам» (6): множество для 1/3, √7 рацион/иррац, поиск иррац., 0.(3)=1/3, ℕ⊂ℝ, целые между √51
- §3 «Свойства корней» (7): √(9·25), √a·√b формула, √(64/16), √(a²)=a (нет), √100·√4, √81/√9, √(36a²)
- §4 «Преобразования» (6): √72=?, 5√3=√?, освобождение 1/√3, 3√2 vs 2√3, √200=?, (√7+√7)²
- §5 «Числовые промежутки» (6): запись x>5, (2;6)∩[4;10], 3∈(2;5], (-∞;0)∪(0;+∞), [1;4)∪[4;8], целые в [-3;4]
- §6 «Системы» (6): {x>2;x≤5}, [x≤1;x>4], -2<3x+1≤7, целые {x≥0;x<4}, {x≥5;x≤3}, {x²>0}
- Финальный босс (7 комбинированных): √(15²+8²), √75−√12, x²=49 число корней, D(√(x-3)+√(7-x)), √(10−2√21), 0.5≤x/3<2, √(0.04·49)

Движок (универсальный):
- 3 типа: select (кнопки), yesno, input (числовой с Enter)
- Полоса прогресса 'N / total'
- 2 попытки → объяснение → опционально пропуск (-5 XP)
- Подсказка -3 XP
- Медали: golden 7/7 без ошибок и подсказок | silver ≥5 | bronze прошёл
- XP: 30 / 50 / 80
- Perfect → доп. ачивка boss_pN_perfect
- 3D-flip анимация медали при награде
- Confetti при ≥4 правильных
- Интеграция с streak, sounds, achievement
- STATE.bossResults сохранён в LocalStorage algebra8_ch1_bossResults
- После прохождения в заголовке карточки отображается медаль + счёт + 'Повторить'

CSS: 52 строки новых стилей через --sec-acc для цветового разделения

Итог: 6829 строк, 11/11 JS-блоков валидны
2026-05-27 13:49:12 +03:00
Maxim Dolgolyov beebdadca0 feat(textbooks): Wave Depth — 4 прокачанных интерактива (+474 строки)
1. §1 «Извлечение в столбик» — пошаговая анимация
   - Поле ввода числа + пресеты 1296/2916/7744
   - Async-функция clStart() рендерит классическое 'деление в столбик'
   - JetBrains Mono шрифт, подсветка текущей грани цветом секции
   - Поясняющий текст для каждого шага рядом
   - При остатке 0: confetti + 15 XP + ачивка 'col-root'
   - Для нецелых корней — корректно показывает остаток

2. §4 «Сравнение через квадраты» — визуальное доказательство
   - 5 пар: 3√2 vs 2√3, 4√3 vs 3√5, √17 vs 4, ...
   - SVG с двумя анимированно растущими квадратами (transform scale 0→1, spring)
   - Победитель — бейдж в верхней части
   - Под квадратами: (3√2)² = 18 > 12 = (2√3)²

3. §5 «Эйлеровы диаграммы» — альтернатива линейной визуализации
   - 4 слайдера для границ A и B
   - Два эллипса (pink/blue) с пересечением
   - Режимы: 'Показать ∪' (золотой контур), '∩' (зелёная штриховка), 'Оба'
   - Дополняет существующую линейную визуализацию

4. §6 «Решатель систем 3+ неравенств» — расширен с 2 до 5
   - Динамический контейнер #sys-list с массивом _sysRows
   - Кнопка '+ Добавить неравенство' (до 5)
   - Кнопка '×' удаляет (кроме первой)
   - SVG-прямая динамически масштабируется под N строк
   - Совместимость с sysMode/solveLin сохранена
2026-05-27 13:39:11 +03:00
Maxim Dolgolyov aed820c2d1 feat(textbooks): красивая анимация доказательства √(ab)=√a·√b
Старая версия: два статичных прямоугольника бок о бок (синий a×b и розовый √(ab)×√(ab)) с текстовым описанием. Зритель не видел РАВЕНСТВА площадей.

Новая версия — настоящее визуальное доказательство:
- Один большой SVG-канвас (600×280) с двумя зонами и стрелкой между ними
- Слева: прямоугольник a×b из единичных клеток (синих). Каждая клетка отдельный <rect> (всего a·b штук)
- Справа: пунктирная рамка квадрата √(ab)×√(ab) (заполнится анимацией)
- При нажатии 'Анимировать':
  * Шаг 1: волна подсветки клеток жёлтым по очереди (20мс задержка)
  * Шаг 2: клетки 'летят' (CSS transition 550мс на x/y) к новой позиции в квадрате,
    меняя цвет с синего на розовый
  * Шаг 3: финальная пульсация + KaTeX-формула с числами и бейдж 'Доказано!'
- KaTeX-формула под канвасом обновляется живо: $\sqrt{a·b}$ = ... + $\sqrt{a}·\sqrt{b}$ = ...
- 'Сбросить' возвращает в исходное положение

Бонус: для непрямого квадрата (a·b не точный квадрат) анимация всё равно работает, клетки плотно укладываются в столбцы по ceil(√ab), визуально показывая что суммарная площадь одинакова.
2026-05-27 13:18:41 +03:00
Maxim Dolgolyov aebdc47e4f fix(textbooks): KaTeX распознаёт \[…\] + переделать «Упрости √» в пошаговую игру
1. KaTeX: в config delimiters добавлены '\['/'\]' (display) и '\('/'\)' (inline) во всех 6 местах вызова renderMathInElement. Раньше initFracIrr использовал \[…\] в template literal — выводилось raw LaTeX. Теперь рендерится математически.

2. «Упрости √» переделан с нуля:
   Было: непонятный drag-and-drop с пустой drop-zone и техническим хинтом
   Стало: явный вопрос 'Выберите точный квадрат, который делит подкоренное'
   - Карточки кандидатов крупные (с подписью "= N²" под числом)
   - Не делит → красная тряска + объяснение
   - Делит но не максимальный → жёлтое предупреждение
   - Максимальный квадрат → зелёная анимация pop + пошаговый вывод KaTeX:
     √72 = √(36·2) = √36·√2 = 6√2
   - confetti + XP +8
   - Кнопка 'Подсказка' даёт намёк
   - На правильном ответе остальные карточки блокируются
2026-05-27 13:14:54 +03:00
Maxim Dolgolyov 6864db5b94 fix(textbooks): подписи на числовой прямой §2 больше не перекрываются
Было: 3 уровня (i%3) × 12px — близко стоящие √2 √3 √5 π √15 наложились друг на друга.

Стало:
- Точки сортируются по координате
- Для каждой подписи ищется минимальный уровень БЕЗ перекрытия с уже размещёнными (с учётом ширины метки ~44px и шкалы в пикселях)
- До 9 уровней по 20px вверх от оси
- От подписи к точке идёт тонкая линия-выноска (0.45 opacity)
- Box-shadow на метках для разделения если плотно

Также: ось перемещена с y=60 на y=100 — больше места сверху для уровней. Контейнер 120 → 140px высоты.
2026-05-27 13:10:20 +03:00
Maxim Dolgolyov 718772a2aa feat(textbooks): переделать 'Связка x² ↔ √x' в наглядный конвейер
Было: два изолированных блока (квадрат и линия), связь неявная.

Стало: конвейер из трёх шагов со стрелками:
  [x] →(возвести в квадрат)→ [x² с площадью квадрата] →(извлечь корень)→ [|x|]

Ключевое улучшение: ползунок теперь от -8 до +8. При отрицательном x:
- площадь всё равно положительная (x²)
- корень даёт |x|, не x
- формула снизу подсвечивается янтарным предупреждением 'это |x| ≠ x'

Под конвейером: живая формула KaTeX типа 'x = 3 → x² = 9 → √9 = 3 ✓'. При отрицательном x текст явно показывает: 'x = -3 → x² = 9 → √9 = 3 ≠ -3 → это |x| = 3'.

Мобайл: вертикальная компоновка со стрелками вниз.
2026-05-27 13:04:37 +03:00
Maxim Dolgolyov 10ba4978cf fix(textbooks): feedback() показывал HTML-сущности как текст
Было: elm.textContent = text — '&#10003; √72 = 6√2' выводилось буквально, а не как '✓ √72 = 6√2'.

Стало: elm.innerHTML = text — entities и теги <b> теперь рендерятся как ожидалось.

Затронуты места где feedback() получал HTML-entities: §4 dragSimp, §3 matchCheck, и др. где успех содержал '&#10003;'.
2026-05-27 13:01:55 +03:00
Maxim Dolgolyov deffa3c714 fix(textbooks): убрать hover-preview карточек §§ — постоянно перекрывал соседей
Сначала пробовали left:105% — лежало на правом соседе.
Затем top:calc(100%+8px) — лежало на нижнем ряду.

Третий вариант (intelligent positioning) был бы over-engineered. Проще — выпилить вообще: карточки и так показывают название и % прогресса (круговой), темы видны в самом параграфе после клика.

Удалено:
- <div class='psel-card-preview'> из innerHTML карточек
- CSS правила .psel-card-preview, .psel-preview-* (оставлен display:none!important на случай если в скриптах ещё ссылается на класс)
2026-05-27 12:57:04 +03:00
Maxim Dolgolyov d0484f9e55 fix(textbooks): убрать вызовы несуществующих initSquares и initRationality
Главная причина почему «Существует или нет?» (§1) не работал:
В buildP1 setTimeout цепочка была:
  initRing() → initCalc() → initSquares() → initExists() → initDual()

initSquares() — функция-не-существует (игра запускается по кнопке через squaresStart). ReferenceError рушил цепочку, поэтому initExists() и initDual() НЕ ВЫЗЫВАЛИСЬ → у dropzones не было event-listeners для drag/click → drag-and-drop не работал.

Та же проблема была в §2 с initRationality() — функция отсутствует, riStart() запускает игру по клику.

Исправил обе цепочки.
2026-05-27 12:48:16 +03:00
Maxim Dolgolyov 62d50e00ae fix(textbooks): infinite loop в §4 dragRender зависал страницу
В 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
2026-05-27 12:44:22 +03:00
Maxim Dolgolyov ed93b696bd fix(textbooks): Алгебра 8 — два бага
1. Hover-preview карточек §§ перекрывал соседнюю карточку
   - Было: position absolute, left:105% — превью §4 ложилось на §5
   - Стало: top:calc(100% + 8px), left:50% center, max-width:90vw
   - Добавлена 'стрелка-носик' указывающая на карточку
   - z-index 100 → 200 (поверх соседей)

2. 'Существует или нет?' в §1 — добавлен click-fallback
   - HTML5 drag&drop сохранён для desktop
   - Альтернатива: клик на корне → клик на зоне (yes/no/обратно в pool)
   - Жёлтый outline для выбранного элемента
   - Hover-эффект на .drag-item (translateY -1px + shadow)
   - Подсказка в widget-описании обновлена
2026-05-27 12:40:54 +03:00
Maxim Dolgolyov 0248e3db61 fix(textbooks): legacy initSearch() больше не бросает TypeError на старте
После Wave 3 поле #search-inp в шапке было заменено на модальный поиск Ctrl+K с #search-modal-input. Но старая функция initSearch() в init() продолжала вызывать getElementById('search-inp').addEventListener(...) — что бросало TypeError на null и крашило init() до построения первого параграфа (отсюда подвисание страницы при загрузке).

Фикс: добавлен guard 'if(!inp) return;' — функция остаётся для обратной совместимости (на случай восстановления старого input).
2026-05-27 12:35:49 +03:00
Maxim Dolgolyov 42408ee301 feat(textbooks): Wave 4 — геймификация Алгебры 8 (+1064 строк, итог 5595)
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-обёртки.
2026-05-27 12:28:44 +03:00
Maxim Dolgolyov 898629a5b6 feat(textbooks): Wave 3 — UX-фичи Алгебры 8 (+636 строк)
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)
2026-05-27 12:18:11 +03:00
Maxim Dolgolyov 1ee16a3a38 feat(textbooks): Wave 2 — прокачка интерактивов Алгебры 8 (+422 строки)
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
2026-05-27 12:08:30 +03:00
Maxim Dolgolyov 0417f51427 feat(textbooks): Wave 1 — визуальная полировка Алгебры 8 (+477 строк)
1. Цветовое разделение по §§: --sec-acc для p1 розовый, p2 фиолетовый, p3 голубой, p4 оранжевый, p5 зелёный, p6 индиго, final янтарный. Применено к .sec-num, .sec-h, .wg, .btn.primary
2. Шрифт Unbounded для всех заголовков (header, секций, hero, card-title, achievement)
3. Watermark-символы: √ ℝ × ↓ [;] { ★ — фоном в каждой секции
4. 3D-тени и translateY(-2px) на .card и .wg при hover
5. Анимированный градиент в hero (heroShift 12s loop)
6. Confetti canvas (70-100 частиц) — при правильном ответе в 14 интерактивах + при достижениях
7. Sparkle-эффект — 5 SVG-точек разлетаются из feedback-элемента
8. Achievement popup — bounce-анимация + pulse-иконка
9. card-icon с outline в цвет секции
10. Mobile polish: sidebar в drawer на ≤768, psel-grid horizontal scroll, padding 12px, шрифты −5-10% на ≤480

Не тронуто: BUILDERS, STATE, achievement logic, goTo, buildPN, классы (.psel-card, .card, .wg, .sidecard), Cache-Control.
2026-05-27 11:56:54 +03:00
Maxim Dolgolyov 8c0506ba23 fix(textbooks): Алгебра 8 — KaTeX в самооценке + щедрая шапка
1. Финальная самооценка (10 вопросов 'Я проверяю свои знания'):
   - Все 10 вопросов и 40 опций переписаны через KaTeX ($...$)
   - Корни, дроби, системы, ℕℤℚℝ, ∞, ≥, √(n-√...) — теперь рендерятся настоящей математической типографикой
   - Пример: было '√2 принадлежит множеству: ℕ ℤ ℚ I' (Unicode) → стало '\sqrt{2} ... \mathbb{N} \mathbb{Z} \mathbb{Q} \mathbb{I}'

2. Шапка с большим воздухом:
   - padding 34/24 → 46/30 (top/bottom)
   - min-height 130px — гарантия не сжаться
   - h1: line-height 1.25 → 1.3, padding-top 2 → 4px
   - sub: line-height 1.35 → 1.4
   - Watermark теперь центрируется через top:50%/translateY(-50%) — больше не лезет на текст
2026-05-27 11:40:51 +03:00
Maxim Dolgolyov 055599bb01 fix(textbooks): KaTeX в финале + анти-кэш в Алгебре 8
1. Финал главы:
   - После buildAssessment() повторный renderMath(body) — захватывает формулы из квиза
   - Дополнительный renderMath через 300ms — на случай если KaTeX не успел загрузиться

2. Мета-теги Cache-Control no-cache, no-store, must-revalidate / Pragma no-cache / Expires 0 — чтобы прежняя версия страницы не зависала в кэше браузера (поэтому и шапка не обновлялась)
2026-05-27 11:36:39 +03:00
Maxim Dolgolyov 5e7098a610 perf(textbooks): lazy-build параграфов Алгебры 8 — стартовая загрузка стала мгновенной
Было: init() синхронно вызывал buildP1...buildFinal — 7 секций × ~500 строк HTML, плюс KaTeX renderMathInElement сканировал весь body. На медленном CPU могло подвисать на 2-5 секунд.

Стало: init() строит только §1 (через goTo('p1')). Остальные секции строятся лениво при первом goTo(id) — кэшируются в BUILT Set.

Профит: первая отрисовка в 7 раз быстрее. KaTeX-рендер тоже только для активной секции.
2026-05-27 11:32:39 +03:00
Maxim Dolgolyov c335f33e25 fix(textbooks): retroactive-фикс существующих достижений Алгебры 8
Прошлый коммит хранил название в Map, но старые записи в LocalStorage (Set из id-ов) подгружались с id в качестве текста — пользователь по-прежнему видел 'ring36', 'start'.

Фикс: словарь ACH_LABELS (id → название) применяется при загрузке как fallback:
- старый формат массив id-ов: id → ACH_LABELS[id]
- новый формат объект {id:text}: если text === id, используем ACH_LABELS[id]

Теперь при следующем открытии учебника старые достижения автоматически получат красивые названия.
2026-05-27 11:30:52 +03:00
Maxim Dolgolyov 0927605bd0 fix(textbooks): не обрезать заголовок Алгебры 8
Заголовок 'Алгебра 8 · Глава 1' визуально обрезался сверху из-за тяжёлого шрифта (font-weight:900) без явного line-height на тесном padding-top 24px.

Фикс:
- padding header: 24px → 34px сверху, 22px → 24px снизу
- h1: добавлены line-height:1.25 и padding-top:2px
- hdr-sub: line-height 1.35, margin-top 4 → 6px
- watermark 'АЛГЕБРА': top -18% → -10%, max font 13rem → 12rem (меньше залезает на текст)
2026-05-27 11:23:41 +03:00
Maxim Dolgolyov 8838f963a3 fix(textbooks): шпаргалка показывает человеч. названия достижений вместо id
Было: 'ring36', 'start' — внутренние id-ы достижений
Стало: 'Начало пути по корням!', 'Нашёл сторону ринга'

STATE.achievements теперь Map(id → text). Старый формат массива id-ов читается с обратной совместимостью (id используется как текст). При сохранении пишется как объект.
2026-05-27 11:21:33 +03:00
Maxim Dolgolyov ea753cf5b0 chore(textbooks): убрать упоминания авторов — учебники теперь как внутренние работы
- Миграция 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 автор больше не отображается под названием (так как поле пустое).
2026-05-27 11:03:25 +03:00
Maxim Dolgolyov fff3ddc45e fix(textbooks): KaTeX-рендер в шпаргалке Алгебры 8
Боковая шпаргалка строилась обычным HTML (Unicode-символы √ ≤ ⊂), формулы не оформлялись как настоящие математические.

Фикс:
- Все формулы в SIDEBARS обёрнуты $-делимитерами KaTeX (\sqrt, \mathbb, \cap, \subset, \Leftrightarrow и т.д.)
- После buildSidebar() вызывается renderMathInElement(box) для встроенного рендера
- Учебник теперь показывает корни и множества в правильной типографике
2026-05-27 09:41:20 +03:00
Maxim Dolgolyov 92a0a364ea feat(textbooks): интерактивный учебник «Алгебра 8 · Глава 1» по Арефьевой/Пирютко
algebra_8.html (3226 строк, 192KB) — полная Глава 1 «Квадратные корни и их свойства. Действительные числа»:

§ 1. Квадратный корень. Арифметический корень:
- Hero «Боксёрский ринг 36 м²» с draggable углом
- Калькулятор √ с проверкой r²
- Игра «Таблица квадратов 10-99» (speedrun, рейтинг в LocalStorage)
- Drag «существует/не существует» для √(-25), √121 и т.д.
- Связка x² ↔ √x с слайдером
- Алгоритм извлечения «в столбик»

§ 2. Иррациональные числа / Действительные числа:
- Анимированная иерархия ℕ⊂ℤ⊂ℚ⊂ℝ
- Drag-сортировка чисел в 4 коробки
- Числовая прямая с √2, √3, √5, π
- Конвертер дробь ⇄ периодическая десятичная
- Пошаговое доказательство √2∈I (5 раскрывающихся шагов)
- Игра «Кто рациональнее?»

§ 3. Свойства корней:
- Геометрическое доказательство √(ab)=√a·√b (SVG)
- Слайдер-проверка свойств (a, b → live)
- Match-игра «выражение ↔ ответ»
- Калькулятор |a|=√(a²)
- Тренажёр «Упрости» (12 задач)

§ 4. Применение свойств:
- Drag «упрости √» (9 заданий, ищи точный квадрат)
- Конвертер a√b ⇄ √c (двусторонний)
- Помощник освобождения от иррациональности (пошагово)
- «Кто больше?» с подсказкой через квадрат
- Тренажёр «a√b» (11 заданий)

§ 5. Числовые промежутки:
- 9 типов промежутков в таблице
- Конструктор промежутка (drag границ, переключение скобок)
- Объединение/пересечение визуально (4 слайдера, 4 промежутка)
- «По картинке — неравенство» (4 задачи)
- «По записи — нарисуй» (4 задачи)

§ 6. Системы неравенств:
- Решатель системы с автоматическим пересечением
- Переключатель «Система ∩ / Совокупность ∪»
- Двойное неравенство как система (пошаговое разложение)
- Игра «Найди целые решения»
- Задача про тарифы 1.382 из учебника

Финал главы:
- Итоговая самооценка (10 заданий с авто-проверкой)
- 3 практические задачи (дорожка с розами, цемент, часовые пояса)
- Историческая справка (Рудольф, Бхаскара, Герон, пифагорейцы)
- Олимпиадная расшифровка кода 25-324-441-64-4-1 → ДРУЖБА
- Метод Герона интерактивно (с итерациями)
- Олимпиадная задача про a+√15 и 1/a−√15

Сквозные фичи:
- KaTeX через CDN, шрифт Inter+Manrope
- Розово-голубая палитра учебника Арефьевой
- Dark mode toggle
- LocalStorage прогресс по §§ + достижения
- Sticky шпаргалка справа (мобильно — снизу)
- Поиск по карточкам параграфов
- Адаптив ≤ 980px
2026-05-27 09:32:09 +03:00