Commit Graph

436 Commits

Author SHA1 Message Date
Maxim Dolgolyov e4b0747dac feat(trainer): V4.1 группа 3 — 20 генераторов (выражения + новые формулировки)
Третья волна плана v4 (упрощение/формулы/степени) + новые форматы условий.
Все «корень-вперёд»; simplify проверяется эквивалентностью, compute — целым.

Упрощение (simplify): simp-like-multivar (две буквы), simp-like-const (со числом),
simp-sub-bracket (вычесть скобку — знаки), simp-distribute-combine (раскрыть+привести),
simp-factor-common (вынести множитель), simp-factor-group (группировка),
simp-eval-at (упростить и вычислить при x=x0).

Формулы (formulas): diff-sq-factor (разложить разность квадратов, обратно),
sq-trinom-factor (свернуть в квадрат), sq-sum-coef (квадрат суммы с коэф.),
cube-sum (куб суммы), sum-cubes-factor (сумма кубов),
fmt-blank-square (вставь число в тождество), fmt-fix-square (найди ошибку).

Степени (powers): pow-div (частное), pow-product-base (степень произведения),
pow-frac-combine (дробь степеней), pow-numeric-laws (произведение степеней числом),
pow-standard-form (стандартный вид), pow-which-law (какой показатель).

Новые ФОРМУЛИРОВКИ: вставь-пропуск, найди-ошибку, какой-закон, упрости-и-вычисли,
разложить (обратные формулы) — разнообразие условий по запросу.

Итого 116 генераторов. Смоук 26148 проверок (simplify: приём канон. answerExpr +
отказ off-by-one; compute: целое+приём; все шаги→LaTeX); геометрия не задета (6968/0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 13:31:38 +03:00
Maxim Dolgolyov 1868e6cb9b feat(trainer): V4.1 группа 2 — 17 генераторов (пропорции/проценты/текстовые)
Вторая волна плана v4. Все compute, «корень-вперёд» → чистые целые ответы,
пошаговые решения; правок движка/страницы нет.

Пропорции (proportions): prop-direct-word (прямая, цена/кол-во), prop-inverse-word
(обратная, рабочие/дни), prop-scale-map (масштаб карты), prop-compound (тройная),
prop-share-ratio (деление в отношении a:b).

Проценты (percents): pct-increase (+p%), pct-decrease (−p%), pct-change (на сколько
% выросло), pct-simple-interest (простые проценты), pct-compound-2y (сложные, 2 года),
pct-restore-before (исходное до повышения — обратный процент).

Текстовые (applied): app-meet (встречное), app-overtake (вдогонку), app-upstream
(против течения), app-work-joint (совместная работа), app-mix-blend (концентрация
смеси), app-profit-pct (прибыль в %).

Итого 96 генераторов. Смоук 16508 проверок (каждый ген: инстанс/самопроверка/приём
канон.ответа/отказ неверного/все шаги→LaTeX); геометрия не задета (6968/0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 13:25:17 +03:00
Maxim Dolgolyov 105e046087 feat(trainer): V4.1 группа 1 — 15 новых линейных генераторов (уравнения/неравенства/системы)
Реализована первая волна плана v4 (разнообразие задач по темам). Все «корень-вперёд»,
ответы чистые целые, подробные пошаговые решения, без правок страницы.

Уравнения (linear-eq): lin-both-frac (с делением на a−c≥2), lin-x-denom (a/(x+b)=c),
lin-k-over-x (k/x=c), lin-abs (|ax+b|=c, два корня, kind roots), lin-frac-eq-frac
(дробь=дробь крест-накрест), lin-nested-paren (вложенные скобки), lin-literal (ax+b=mx,
«выразите x»).

Неравенства (inequalities): ineq-both-sides (ax+b<cx+d), ineq-both-flip (смена знака,
c>a), ineq-paren (a(x+b)>c), ineq-count-int (число целых решений lo<x≤hi — двойное
неравенство через compute, обход ограничения одной полупрямой).

Системы (systems): sys-sum-diff (x+y=S, x−y=D), sys-subst (подстановка y=mx+k),
sys-word (текстовая на 2 неизвестных), sys-3x3 (3×3 тизер, answerVars длины 3).

Движок: минимальная аддитивная правка — kind system теперь показывает gen.display
(сюжетную формулировку), если он задан, вместо \begin{cases} (только для sys-word;
существующие sys-2x2/neg без display не затронуты). Итого 79 генераторов.

Верификация: смоук 8314 проверок — каждый генератор инстанцируется по сидам,
самопроверяется, принимает канонический ответ и отвергает неверный, ВСЕ шаги решения
рендерятся в LaTeX; геометрия не задета (6968/0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 13:16:37 +03:00
Maxim Dolgolyov 1dcde8790a feat(trainer): тема «Окружность» + режим «читать условие с чертежа»
Окружность (новая геом-тема g-circle, 9 кл, π ≈ 3,14 → ответ — конечная
десятичная дробь, ученик вводит её): 4 генератора — длина окружности по радиусу
(2πr), по диаметру (πd), площадь круга (πr²), длина дуги ((n/360)·2πr, n=45·k,
require r·k чётно → дробь конечная). Новые типы фигур: circle (радиус/диаметр/
заливка) и circle-arc (два радиуса под центральным углом + выделенная дуга).

Режим «читать значения с чертежа»: у всех 19 геом-генераторов добавлено
figurePrompt (краткое условие); переключатель «Текст / На чертеже» (#tr-figmode)
на странице, выбор сохраняется в localStorage. В режиме чертежа числа берутся с
фигуры, текст минимальный. Движок прокидывает figurePrompt; showStatement
выбирает полный текст или промпт; renderFigureToggle показан только для задач с
чертежом; для текстовых/алгебраических задач режим скрыт, проверка ответа от
режима не зависит. На чертеж n-угольника выведено число сторон (n = …).

Верификация: headless-смоук 6968 проверок / 1140 рендеров; ответы окружности
конечные и принимаются движком (1600 экземпляров, округление до 2 знаков ok);
inline-скрипт парсится; адверсариал-ревью — circle clean, toggle без high/medium
(2 low устранены: скрытие тумблера при неудаче генерации + подпись n сторон).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 12:49:15 +03:00
Maxim Dolgolyov ff9900bdcc feat(trainer): геометрические чертежи задач — движок фигур + иллюстрации во всех геом. темах
TrainerFigures (frontend/js/trainer/figures.js) — безопасный SVG-рендер
«фигуры как данные» (модель SimForge): 11 типов — прямоугольный треугольник,
углы треугольника/смежные/внешний, прямоугольник, квадрат, треугольник по
основанию и высоте, трапеция, параллелограмм, ромб, правильный n-угольник,
подобные треугольники. Чертёж строится из чисел (params),  без eval/Function,
подписи экранируются, искомая величина — «?». Белые штрихи под индиго-сцену.

- generators.js: figure-спека на всех 15 геом-генераторах (Углы, Пифагор,
  Площади, Многоугольники, Подобие) — привязка размеров к параметрам задачи.
- _trainer_engine.js: figure прокидывается в problem.
- trainer.html: контейнер #tr-figure в шапке-герое, renderFigure() в newProblem,
  скрыт для текстовых задач, скрипт-тег, CSS.

Верификация: headless-смоук 5489 проверок / 900 рендеров (нет NaN/<script>/
обработчиков, «?» на искомой); адверсариал-ревью 4/4 группы clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 12:23:03 +03:00
Maxim Dolgolyov b5916e7f3b feat(trainer): полные решения систем + шапка «Математика»; решения по всем темам
- Шапка-пилюля теперь УНИВЕРСАЛЬНАЯ и не мигает: всегда «Математика · 5–9 класс» (не переключается на «Алгебра»; предмет выбирается сегментом Алгебра/Геометрия)
- Системы 2 ур-ний — ПОЛНОЕ решение методом сложения (6 шагов): уравнять коэффициенты при x, вычесть (исключить x → coefY·y=rhsY), найти y, подставить, найти x, ответ-пара. Коэффициенты 2..4 / |коэф|≥2 — без «1x» в шагах
- Аудит решений по ВСЕМ темам: 7 «тонких» (1 шаг) генераторов (simp-like/expand, pow-mult/pow, sq-sum/diff, diff-sq) развёрнуты в 2 шага (правило → итог)
- смоук T21: у каждого из 60 генераторов решение ≥2 шагов; движок 1214/1214, страница 42/42; эмодзи 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 18:05:43 +03:00
Maxim Dolgolyov fb16821b0a feat(trainer): НОД/НОК с нормальными числами; универсальная шапка; НОК теперь появляется
НОД/НОК (числа больше + без степеней):
- движок: фича factorize ({name, of}) кладёт в шаги решения СТРОКУ разложения на простые множители без степеней (36 -> «2*2*3*3»); helper primeFactorString
- генераторы: a=g·m, b=g·n (g,m,n из 2..9) -> нормальные числа (14, 35, 16, 112…), общий множитель гарантирован; решение показывает разложение обоих + НОД/НОК = произведение множителей
- пример: 16 = 2·2·2·2, НОК = 2·2·2·2·7 = 112

НОК теперь появляется (раньше показывался только НОД):
- причина: smart-подбор брал первый неосвоенный навык ГЛОБАЛЬНО -> из НОД прыгал на lin-basic, НОК не доходил
- фикс: умная тренировка теперь адаптируется В ПРЕДЕЛАХ выбранной темы (pickNext scope = skillsOf(curTopic)) -> в теме «НОД и НОК» ведёт по обоим навыкам; тему выбирает ученик в рейле

Шапка: пилюля стала универсальной и динамической (updateSubjectPill: «Алгебра · 5–9 класс» / «Геометрия · 7–8 класс» по текущему предмету), вместо статичной «Алгебра · 7–8 класс».

Смоук движка 1154/1154, страница 42/42; эмодзи 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:57:30 +03:00
Maxim Dolgolyov 2a50ff740a fix(trainer): решение НОД/НОК без степеней — произведением простых множителей
По просьбе: показывать разложение без показателей (2·2·7-стиль вместо 2²·7). Из-за статичных шаблонов переменное число множителей со степенями не развернуть, поэтому модель упрощена и стала нагляднее:
- a = p·q, b = p·r (три различных простых из {2,3,5,7}); общий множитель p стоит ПЕРВЫМ в обеих строках разложения → общее видно сразу
- НОД = p (общий множитель), НОК = p·q·r (все множители, общий один раз)
- разложение печатается как 10 = 5·2, 15 = 5·3 → НОД = 5; НОК(6,15): 3·2·5 = 30 — без степеней
- gcd/lcm дают эталон проверки; смоук движка 1154/1154, эмодзи 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:44:20 +03:00
Maxim Dolgolyov 664751e273 feat(trainer): красивое решение НОД/НОК — через разложение на простые множители
- НОД/НОК переписаны: число строится из двух простых p<q (из {2,3,5,7}) со степенями 1..2 (a=p^e1·q^f1) → разложение известно из параметров. Решение показывает СТАНДАРТНЫЙ школьный метод: разложить оба числа, НОД = общие множители в наименьших степенях, НОК = все в наибольших. Пример: 225=3²·5², 45=3²·5 → НОД=3²·5=45
- выбор простого — тернарником в derive (ip/iq, НЕ pi — pi это π в SimExpr!)
- exprToLatex: x^1→x, x^0→1 (чтобы 7^1 печаталось как 7) + ставит · между числовыми множителями (2·7², а не слипшееся «27²»); алгебраическое неявное умножение (2x, 3(x+1)) сохранено
- gcd/lcm дают эталон для проверки, min/max — степени для шагов
- смоук движка 1154/1154, страница 40/40; эмодзи 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:39:21 +03:00
Maxim Dolgolyov a7d20a0c90 feat(trainer): НОД и НОК (5-6 кл) + функции gcd/lcm в SimExpr
- SimExpr: добавлены whitelisted-функции gcd (алгоритм Евклида) и lcm (арность 2), защищены от NaN/0/отрицательных. Аддитивно — существующие спеки SimForge/Quantik не затронуты
- НОВАЯ тема НОД и НОК: gcd-pair (НОД), lcm-pair (НОК). Числа строятся как g·m и g·k (общий множитель) → НОД нетривиален; gcd/lcm считаются и проверяются движком
- 60 генераторов, 20 тем; смоук движка 1154/1154, страница 40/40; gcd(36,24)=12, lcm(4,6)=12 верны; эмодзи 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:30:36 +03:00
Maxim Dolgolyov 169501f12a feat(trainer): контент 5-6 кл — десятичные дроби + отрицательные числа
- НОВАЯ тема Десятичные (5 кл): сложение/вычитание/умножение десятичных. Строятся через десятые/сотые (целые ÷10, ÷100) → ответ печатается чисто, без float-мусора (0.8, 0.07, 0.16)
- НОВАЯ тема Отрицательные (6 кл): сумма/разность/произведение с отрицательными. Словесные формулировки («Найдите сумму чисел -8 и 9», «Из числа 6 вычтите -12») — без двусмысленных операторов; constraint гарантирует хотя бы одно отрицательное
- всё kind compute (движок не трогал); LEVELS проставлены
- 58 генераторов, 19 тем; смоук движка 1114/1114, страница 40/40; эмодзи 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:17:22 +03:00
Maxim Dolgolyov db5c6b80ec feat(trainer): контент — геометрия (площади/многоугольники/подобие) + дроби 5-6
- Площади (+3): трапеция (a+b)/2·h, параллелограмм a·h, ромб d1·d2/2
- НОВАЯ тема Многоугольники (8 кл): сумма углов 180·(n−2), угол правильного n-угольника
- НОВАЯ тема Подобие (8 кл): сходственная сторона/периметр по коэффициенту
- НОВАЯ тема Дроби (5 кл): часть от числа (целый ответ), сложение дробей с одинаковым знаменателем (дробный ответ — ученик вводит «7/4» или «1.75», SimExpr считает)
- всё kind compute (новой движковой работы нет); LEVELS проставлены
- 52 генератора, 17 тем (геометрия: Углы, Пифагор, Площади, Многоугольники, Подобие)
- смоук движка 997/997, страница 40/40 (T1 целочисленность теперь только для integerAnswer-генераторов — дроби легитимны); эмодзи 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:04:39 +03:00
Maxim Dolgolyov fb81beca39 feat(trainer): разбор типовых ошибок (репетитор C1, движок)
- TrainerEngine.analyzeMistake(problem, value) -> {type, hint} | null: по неверному числовому ответу распознаёт типовую ошибку и даёт адресную подсказку, НЕ выдавая ответ
- solve: уравнение восстанавливается как линейное f(x)=A·x+B по двум точкам (без структуры генератора) -> ловит «забыл разделить на коэффициент»
- общие эвристики: перепутан знак (value≈-correct), близкая арифметическая ошибка (|Δ|≤20%), иначе generic
- работает для solve/compute; пара/корни/неравенство пропускаются
- смоук движка 825/825 (T20: nodivide/sign/arith/generic/null)
- страница НЕ тронута (редизайн в параллельной сессии); показ подсказки на неверном ответе подключу на странице вместе с полировкой ввода систем после редизайна

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:52:00 +03:00
Maxim Dolgolyov 5226deb975 feat(trainer): системы 2 уравнений (kind system, пара-ответ) + текстовые задачи
- НОВЫЙ kind system: движок строит \begin{cases}, хранит пару {x,y}, самопроверка подстановкой обоих уравнений; checkStudentAnswer._checkSystem парсит «x=2; y=3» или «2; 3» (метки опциональны), проверяет ОБА уравнения
- тема Системы: sys-2x2 (полож. коэф., ур.2) + sys-2x2-neg (отрицательные, ур.3); приём корень-вперёд (берём решение, выводим правые части, det≠0)
- тема Задачи (compute, текстовые семьи): движение (путь/время/скорость), сплав (%), цена со скидкой
- exprToLatex: единичный коэффициент 1*x->x, -1*x->-x (латентная недоработка)
- 43 генератора, 14 тем; смоук движка 817/817 (T19 системы + T19b текстовые)
- страница (trainer.html) НЕ тронута — её редизайнит параллельная сессия; полировка ввода систем (скрыть «x=», placeholder, фидбэк пары) — после редизайна. Системы уже работают через checkStudentAnswer (ввод «2; 3»)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:44:19 +03:00
Maxim Dolgolyov f0af2079c3 fix(trainer): сложность = структура задачи, а не масштаб чисел
Пользователь верно заметил: масштабирование чисел (больше/меньше) — не настоящая сложность. Настоящая = больше действий, скобки, дроби, переменная в обеих частях.

- генераторы размечены структурным level 1-3 (generators.js, LEVELS): напр. Уравнения ax+b=c (1) -> a(x+b)=c (2) -> a(x+b)=c(x+d) (3); Степени: вычислить -> произведение -> степень степени
- контрол сложности выбирает ВАРИАНТ-генератор нужного уровня в теме (pickByLevel с клампом к доступным), а не масштабирует числа
- клик по чипу навыка закрепляет конкретный вариант (pinned); Авто = адаптивный подбор (умная тренировка от простого к сложному) + показ ур.N текущего
- кросс-тематический адаптив pickNext — только в Авто без закрепления
- движковое _scaleRange/level оставлено как capability (T18), страница его НЕ использует
- смоук движка 682/682, страница 36/36 (Сложный->ген ур.3, Лёгкий->ур.1); эмодзи/eval 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:24:31 +03:00
Maxim Dolgolyov d07cb2a434 feat(trainer): уровни сложности заданий (Лёгкий/Средний/Сложный + Авто)
- движок: instantiate(gen,{level}) масштабирует диапазоны pick (_scaleRange): L2=база, L1 меньше магнитуды/меньше отрицательных, L3 шире → сложнее; универсально для всех генераторов (корень-вперёд + самопроверка держат корректность), opt-out gen.noScale; generateBatch прокидывает level
- страница: контрол «Сложность: Авто / Лёгкий / Средний / Сложный» в рабочей зоне; «Авто» поднимает уровень с серией верных (streak≥2→2, ≥4→3, ошибка→1); скрыт для текстовых задач из банка
- смоук движка 682/682 (T18: 36 ген × L1/L2/L3, L3 шире L1, L2==база), страница 34/34; эмодзи/eval 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:10:28 +03:00
Maxim Dolgolyov 6eaf68a158 feat(trainer): контент по программе учебников + геометрия + фильтр предмета
- классы тем выровнены по нашим учебникам (степени/формулы/упрощение/неравенства=7, пропорции/проценты=6, квадратные=8, прогрессии=9)
- Прогрессии (9 кл): n-й член арифм./геом. прогрессии (compute)
- ГЕОМЕТРИЯ (subject geometry): Углы (сумма углов треугольника, смежные, внешний — 7 кл), Пифагор (гипотенуза/катет через тройки — 8 кл), Площади (прямоугольник/треугольник/квадрат — 8 кл)
- 36 генераторов, 12 тем; всё kind compute (числовой ответ, проверка подстановкой, sqrt в SimExpr)
- страница: фильтр предмета Алгебра/Геометрия (segmented), синхрон с adaptive/ручным выбором; иерархия Предмет → Тема → Навык
- смоук движка 572/572, страница 33/33; эмодзи/eval 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:59:11 +03:00
Maxim Dolgolyov 47d4f71eac feat(trainer): P10 — контент 8 класса (степени, формулы, неравенства)
- новый тип kind:inequality: answerRel{op,bound}, парсер отношения (_parseRel/_checkInequality) — нормализация «x op c», приём обратной записи, сверка op+границы; self-check внутри/снаружи решения
- темы: Степени (aⁿ, xᵃ·xᵇ, (xᵃ)ᵇ), Формулы сокр. умножения (квадрат суммы/разности, разность квадратов), Неравенства (вкл. смену знака при делении на отрицательное) → 26 генераторов, 8 тем
- движок: simplify рендерит выражение в KaTeX (exprToLatex(srcExpr)); неравенство — в KaTeX с отношением; fallback-display учитывает op
- страница: ввод/лейбл для неравенств, isLabelKind
- смоук движка 397/397 (T15 неравенства, T16 степени/формулы; T3 ≥10 для малых пространств), страница 33/33; ROADMAP_V2 P10 → DONE

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:20:45 +03:00
Maxim Dolgolyov 277bddf1fd feat(trainer): P7 пошаговое решение (репетитор) + P8 мат-клавиатура
- движок checkStep(problem, line): шаг = равносильное уравнение (держится во всех корнях И не выполняется в не-корнях) → ловит арифметику, потерю корня, тождество; статусы equivalent/solved/wrong/identity/parse
- страница: тумблер «Решить по шагам» (kind solve), ввод и проверка каждого шага, список принятых шагов (KaTeX + галочка), подсказка следующего шага, завершение по solved-форме; общий onSolved; stepPref между задачами
- P8: экранная мат-клавиатура (( ) x / ^ √ ; ⌫, вставка в курсор, без либ) + live-превью KaTeX; для поля ответа и поля шага
- ROADMAP_V2: P7+P8 → DONE; смоук движка 300/300 (T14 checkStep), страница 33/33 (шаг-сценарии)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:06:46 +03:00
Maxim Dolgolyov 7cc2a9d526 feat(trainer): P5 — несколько корней, эквивалентность выражений, новые темы
- движок: gen.answers → несколько корней (_checkMultiRoot, ввод через «;», сверка мультимножеством)
- kind simplify: эквивалентность выражений численным сэмплингом (_sampleEquiv, _checkEquiv), фикс. точки без Math.random
- exprToLatex: знаковые коэффициенты — -5x, x²−5x+6, a−(−b)→a+b (вынос ведущего минуса, схлопывание)
- темы: Упрощение (подобные, скобки) + Квадратные (Виета x²+bx+c=0, разность квадратов) → 17 генераторов, 5 тем
- страница: префикс «x=»/подсказка ввода и ответ-лейбл по типу задачи
- смоук движка 291/291 (T11 roots, T12 simplify, T13 latex), страница 26/26, adaptive 12/12; план P5 → DONE

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 14:15:21 +03:00
Maxim Dolgolyov 48a73d9f8e feat(trainer): P2 — умная тренировка, интервальное повторение, итог сессии
- adaptive.js (TrainerAdaptive): nextSkill (in-session повтор → серверный due → прогрессия → удержание), onWrong/onCorrect (очередь повторения), sessionStats
- умная тренировка на странице (тумблер, по умолч. вкл): авто-подбор навыка от простого к сложному, возврат ошибок
- сессия из 10 задач + экран «Итог сессии» (верно/точность/навыки/стоит повторить); неверный ответ авто-показывает решение
- сервер: SR-поля box+due_at на practice_progress (мигр.082, Leitner 0/1/3/7/16/30 дн), listProgress отдаёт box/due_at/due
- смоуки: adaptive 12/12, страница 23/23, practice.test.js 11/11 (+SR box/due); план P2 → DONE

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 13:46:29 +03:00
Maxim Dolgolyov 20b8ce2c5b feat(trainer): P1 — темы/навыки, +8 генераторов, подробные пошаговые решения
- таксономия тема→навык (topics/byTopic), метаданные topic/order/subject/grade
- 13 генераторов в 3 темах: Уравнения (+a(x+b)=c(x+d), (ax+b)/c=d), Пропорции (3), Проценты (3)
- проценты как compute-задачи: текстовый prompt + проверка подстановкой (latex уравнения скрыт)
- подробные объяснения: каждый шаг расписан словами + шаг «Проверка» (подстановка корня)
- UI: вкладки тем + чипы навыков, бейджи мастерства, авто-выбор первой неосвоенной темы/навыка
- движок: exprToLatex чинит отрицательные множители (7·(−5)), поле kind, нумерованные шаги решения
- смоуки 238/238 (движок) + 19/19 (страница); план: P1 отмечен DONE

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 13:29:44 +03:00
Maxim Dolgolyov c370eaa803 feat(trainer): ИИ-тренажёр — генераторы задач + SimExpr-верификатор, прогресс, фича-флаг
- движок _trainer_engine.js: instantiate/generateBatch/verifyRoot/checkStudentAnswer/exprToLatex
- 5 генераторов уравнений 7 класса (generators.js), приём «корень-вперёд» → целые ответы
- страница /trainer: KaTeX-рендер, чипы-темы, мгновенная проверка, подсказка/решение, авто-выбор навыка
- прогресс practice_progress (мигр.081) + /api/practice/progress|attempt + LS.practiceProgressList/Submit
- фича-флаг trainer: тумблер в админке (Модули), requireFeature, FEATURE_HREFS (скрытие сайдбара+редирект), MODULE_CATALOG
- fix: подключён Lucide CDN на странице (иначе иконки сайдбара пустые)
- тесты practice.test.js (10/10); план развития plans/ai-trainer/PLAN.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 13:11:47 +03:00
Maxim Dolgolyov e38abff02a feat(assistant): срок жизни диалога — 7 дней без общения
saveChat пишет метку времени, loadChat сбрасывает диалог, если с последней
реплики прошло больше CHAT_TTL (7 дней). Обратная совместимость со старым
форматом-массивом. Сноска обновлена.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 23:07:11 +03:00
Maxim Dolgolyov 7c0ccc7282 feat(assistant): диалог сохраняется между заходами (localStorage, per-user)
_chat теперь персистится в localStorage (ключ asst_chat_<uid>, последние 30
сообщений) при каждом ответе и восстанавливается при загрузке виджета. Живёт,
пока ученик сам не нажмёт «Очистить» (чистит и хранилище). Сноска обновлена.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 23:04:43 +03:00
Maxim Dolgolyov c045ea02b0 feat(assistant): больше рабочей памяти (~14 сообщений) + оформление сноски
История разговора расширена с 6 до 14 сообщений (фронт _chat.slice и бэкенд
история в ask/askStream). Сноска о памяти переоформлена: иконка-история + чище
текст, акцент на количестве.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 22:56:51 +03:00
Maxim Dolgolyov b310029e8d style(assistant): компактные режимы — иконка + короткая подпись
Кнопки режимов перерисованы как тулбар: вертикально иконка (inline SVG .ic) +
лаконичная подпись. Длинные ярлыки сокращены (Проверить решение -> Проверить,
Тест в банк -> В банк, Нарисовать -> Рисунок); полный смысл в title. Рендер
data-driven из MODE_DEFS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 22:54:19 +03:00
Maxim Dolgolyov 29fc270c0e fix(assistant): «Забыть всё» теперь сбрасывает и производный профиль
clearMemory ставит точку отсчёта asst_forget_<uid> (datetime now); слабые
предметы/темы в _studentProfile считаются только по активности после неё, так
что панель памяти видимо очищается. Кнопка «Забыть всё» в виджете показывается
лишь при наличии заметок/слабых тем, профиль помечен как авто-обновляемый.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 22:36:29 +03:00
Maxim Dolgolyov 7b4a274aed feat(assistant): умная память Квантика — свежесть, категории, темы по всем предметам
Память об ученике (1+2+3 из плана), всё строго на русском:
- СВЕЖЕСТЬ: эффективный вес заметок с затуханием по времени (полураспад ~31 день),
  в промпт идут только актуальные (порог по effWeight). Старое тихо тает.
- УМНОЕ СЛИЯНИЕ: вместо дедупа по первым 24 символам — стем-токены (русская
  морфология) + Jaccard; похожие заметки сливаются (вес+, текст освежается),
  а не плодят дубли. Лимит 18.
- КАТЕГОРИИ: экстрактор классифицирует факт (трудность/предпочтение/цель/
  сильная сторона/личное), возвращает JSON; запоминаются и сильные стороны/
  интересы, не только проблемы. Гард по кириллице — не-русский текст не попадает.
- ТРУДНЫЕ ТЕМЫ ПО ВСЕМ ПРЕДМЕТАМ: профиль считает слабые темы из user_answers+
  topics (любой предмет, русские названия), объединяя с экзаменом, а не только math9.
- UI «Что я о тебе помню»: у заметок русская плашка-категория.
Без миграции (колонки kind/weight/updated_at уже есть). Проверено: логика 8/8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 22:27:28 +03:00
Maxim Dolgolyov 4c9656e8a8 style(assistant): вау-уровень окна Квантика
- стекло-блюр фона окна (backdrop-filter blur+saturate), глубже тень;
- полноширинная градиентная шапка (фиолетово-бирюзовый) со скруглением;
- аватар с зелёным «онлайн»-пульсом;
- анимированный индикатор печати (три прыгающие точки) вместо текста Думаю;
- плавное появление каждого сообщения (slide-in).
Всё уважает prefers-reduced-motion. Только frontend/js/assistant.js, inline SVG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 22:17:55 +03:00
Maxim Dolgolyov b6c08f1b16 style(assistant): премиальный редизайн окна Квантика
Окно «Спроси Квантика» — удобнее, красивее, солиднее:
- шире (418px), многослойная мягкая тень с фиолетовым отливом, скругление 20px;
- настоящая шапка с аватаром в круге (градиентный фон) и разделителем;
- пузыри сообщений: ученик — фиолетовый градиент с хвостиком, ассистент —
  светлая карточка с тонкой рамкой; мягкая тень;
- поле ввода с фокус-кольцом + отдельная кнопка отправки (стрелка-самолётик),
  градиентная — удобнее на телефоне и нагляднее;
- режимы и чипы — мягкие пилюли, активный режим градиентный с тенью;
- область чата выше (54vh) с аккуратным фиолетовым скроллбаром.
Только frontend/js/assistant.js, без эмодзи (inline SVG).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 22:13:02 +03:00
Maxim Dolgolyov 1915026bff feat(assistant): авто-переиндексация системы при смене флагов + пометка «без описания»
- updateFeatures вызывает _autoReindexSystem(): при тоггле любого модуля снимок
  знаний о системе обновляется сам (только если уже индексировали — не создаёт
  KB на пустом месте). Кнопку жать больше не нужно после смены флагов.
- getAssistant отдаёт systemUndoc — модули с фича-флагом, но без записи в
  каталоге; админ-карточка показывает «Без описания: …» (пассивная подсказка,
  без пушей), чтобы при желании дополнить «Описание системы».

Проверено: авто-реиндекс (не создаёт пустой / обновляет существующий) + undoc 3/3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 21:34:43 +03:00
Maxim Dolgolyov 08da26afca feat(assistant): индексация системы из админки — Квантик знает актуальные модули
Кнопка «Сохранить и проиндексировать систему» в /admin#assistant собирает снимок:
- статус модулей по фича-флагам (что ВКЛЮЧЕНО/ВЫКЛЮЧЕНО сейчас) + каталог разделов;
- редактируемое «Описание системы» админа.
Снимок кладётся в app_settings.assistant_system_kb и подмешивается в ответы:
systemContext(q) ищет по знаниям (стем-префикс под русскую морфологию) и
добавляет в контекст — Квантик опирается на актуальное состояние и не предлагает
отключённое.

Бэкенд: MODULE_CATALOG + buildSystemKb + indexSystem (POST /admin/assistant/index-system),
saveAssistant(+systemDoc), getAssistant(+systemDoc/Count/At), systemContext в ask и askStream.
Клиент: LS.adminAssistantIndexSystem. Без миграции (хранение в app_settings).
Проверено: логика снимка/поиска 5/5, node --check всех файлов.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 21:27:53 +03:00
Maxim Dolgolyov 4be7f9a07c fix(lab-organic): жидкость в пробирке не вылезает за стекло
Клип жидкости был прямоугольник на всю ширину + полукруг — вместе они
шире трубки в зоне закруглённого дна, и заливка торчала за стеклом.
Заменён на путь по внутреннему контуру пробирки (прямые стенки +
дугообразное дно, тем же радиусом w/2-4, что и обводка стекла).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:45:02 +03:00
Maxim Dolgolyov 7829360391 fix(lab-organic): колбы больше + гарантированно в зоне видимости
Пробирки «Качественных реакций» масштабируются по высоте канваса
(190..340px вместо фикс. 150, шире), а вертикальная позиция ty
клампится (≤210) — даже при некорректно большой высоте канваса
колбы всегда остаются в верхней видимой части, а не уезжают за экран.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:38:54 +03:00
Maxim Dolgolyov 09dc62dc96 fix(lab-organic): колбы «Качественных реакций» уезжали за пределы экрана
Canvas был прямым flex-ребёнком (flex:1). _drawQual выставляет
c.height = rect.height*dpr → intrinsic-высота канваса росла, а при
min-height:auto flex-элемент не сжимался ниже неё → разгон высоты
(×dpr на каждую перерисовку). Пробирки центрировались по H/2 и
оказывались далеко ниже видимой области.

Канвас обёрнут в position:relative;flex:1;overflow:hidden и сам стал
position:absolute (как рабочий канвас конструктора молекул) —
intrinsic-размер больше не влияет на раскладку.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:34:58 +03:00
Maxim Dolgolyov 8303d483cc fix(labs): SVG-стрелки уравнений рисовались как сырой текст на canvas
Уравнения реакций содержат inline <svg class=ic> стрелки. На canvas
(fillText) разметка показывалась буквально. Добавлен общий хелпер
ChemVisuals.cleanIcons (SVG→Unicode →/↑/↓), применён в flask (eq),
redox (s.txt) и chemsandbox (ответ квиза — был единственный незакрытый
путь мимо _csClean).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:31:35 +03:00
Maxim Dolgolyov 59ea5e7d65 fix(lab-measure): оверлей измерений на весь экран (SVG не растягивался по inset)
#lm-svg — это <svg> (заменяемый элемент с intrinsic 300x150); inset:0 без явных
размеров его не растягивал, поэтому линейка/угол рисовались за пределами видимой
области и казались нерабочими (панель-div при этом видна). Добавлены width:100vw;
height:100vh — оверлей теперь покрывает вьюпорт, инструменты видны и перетаскиваются.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:26:31 +03:00
Maxim Dolgolyov 254f373522 fix(lab-measure): «Скрыть» (×) закрывает панель целиком, а не только оверлей
Кнопка off раньше прятала линейку/угол, но оставляла тулбар на экране —
теперь снимает и bar (полное закрытие, как ожидается от ×).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:17:22 +03:00
Maxim Dolgolyov cd0ce17a60 feat(lab-graph): удобство и красота — скрытие функций, точки, контролы вида, пинч
«График функции», большой апгрейд UX:
- у каждой функции кнопки «глаз» (скрыть/показать, не удаляя) и «очистить»;
  скрытая — приглушена и зачёркнута, исключается из графика/hover/значений
- плавающие контролы вида поверх canvas: зум +/−, сброс вида, тумблер «Особые точки»
- ОСОБЫЕ ТОЧКИ: нули функций, y-перехваты и пересечения кривых — ringed-точки
  с подписью координат (бисекция по смене знака; правка: точные нули на узлах
  сетки больше не теряются; дедуп; подписи скрываются при «частоколе» >22 точек)
- пинч-зум двумя пальцами к центру жеста (к 1-пальцевой панораме)

Движок: setHidden/setShowPoints/_drawPoints/_findZeros/_visible; hover и
инфобар уважают скрытие. Только фронт. node --check OK; zero-finder 5/5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:11:21 +03:00
Maxim Dolgolyov fa29332bcd feat(lab-graph): введённые функции — редактируемое KaTeX-поле
Введённая функция показывается отрисованной формулой KaTeX прямо в строке;
клик по формуле → правка текста на месте (raw input + живое превью под полем),
клик мимо/blur → снова формула. Реализовано без MathQuill: .fn-field держит
<input> и .fn-math (KaTeX), класс has-math переключает отображение по фокусу.

- renderFnMath() рисует формулу в строке; _fnDisplay() решает режим (фокус+значение)
- focus/blur/mousedown-обработчики в _initGraphPanel (идемпотентно)
- живое превью .fn-preview теперь видно ТОЛЬКО при правке (:focus-within), цвет функции
- graphInsert/applyPreset/state-apply/clearAll/default-fn0 обновляют math-поле
- _katexInto() — общий безопасный рендер

Только фронт. node --check OK; логика вставки 5/5 (прошлый прогон).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:02:08 +03:00
Maxim Dolgolyov 000e42f9b3 feat(lab-graph): KaTeX-формулы + панель ввода как редактор формул
«График функции»:
- примеры (чипы) и живой предпросмотр каждой функции рендерятся в KaTeX
  (data-tex на чипах, _initGraphPanel рендерит при открытии)
- предпросмотр теперь всегда виден, крупный и в цвет функции; пустое поле
  показывает плейсхолдер-формулу приглушённо
- НОВОЕ: keypad вставки структур (x², xⁿ, √, a/b, |x|, π, sin/cos/tg/ln/eˣ, ())
  — клик вставляет в активное поле по каретке (как редактор формул в PowerPoint)
- graphInsert(token) с маркером каретки |; активное поле отслеживается по focus

Только фронт. Проверено: node --check, логика вставки 5/5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 18:56:31 +03:00
Maxim Dolgolyov c49077abbc feat(assistant): живость питомца — лицо реагирует на диалог (фича 6/6)
Лицо Квантика в шапке чата (PetSprite) меняет настроение по состоянию:
- думает (нейтральное + лёгкая анимация-покачивание asstThink) пока ждём/стримим
- радуется (happy) на готовый ответ; грустит (sad) на ошибку/лимит/«не нашёл»
- ликует (ecstatic) на сгенерированный тест и нарисованную картинку
Вплетено в send/sendNonStream/makeQuiz/drawInChat через setNameFace().
Анимация уважает prefers-reduced-motion. Только frontend.

Серия из 6 фич доработки Квантика завершена (стриминг, контекст урока,
сократический режим, авто-здоровье провайдеров, генерация тестов, живость).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:12:49 +03:00
Maxim Dolgolyov 78aea47619 feat(assistant): генерация тестов в банк вопросов (фича 5/6)
Учитель: режим «Тест в банк» в Квантике — тема/текст превращается ИИ в вопросы
с выбором ответа, ревью в чате (варианты, верный подсвечен, пояснение),
кнопка «Сохранить в банк» (выбор предмета + тема) создаёт их через POST /questions.

Бэкенд: questionsFromText (по образцу flashcardsFromText, надёжный парс JSON
с починкой обрезанного) + роут POST /assistant/questions (requireRole
teacher/admin, fcLimiter). Клиент: LS.assistantQuestions. Виджет: режим quiz
только для учителя + makeQuiz (рендер и сохранение через createQuestion/getSubjects).

Проверено на живом шлюзе: 5 валидных вопросов, верный индекс в диапазоне.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:09:02 +03:00
Maxim Dolgolyov bc0ed1892f feat(assistant): авто-здоровье провайдеров + ручная проверка (фича 4/6)
Новый модуль assistant-health.js (по образцу classroom-cleanup): каждые 15 мин
пингует каждого провайдера (pingLLM) → app_settings.assistant_health
{ id:{ok,at,error,ms,fails} }. Авто-понижение: если активный провайдер
не отвечает 2+ раза подряд, а есть здоровый рабочий запасной — автоматически
переключает assistant_active и пишет assistant_failover (баннер «health»).
schedule() из server.js (unref).

Админка: тумблер «Авто-проверка провайдеров», кнопка «Проверить сейчас»
(POST /admin/assistant/health → runHealth), цветной индикатор здоровья на
каждой карточке провайдера (зелёный/красный + время/ошибка в title).
keyless-шлюзы и провайдеры без ключа учтены.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:02:37 +03:00
Maxim Dolgolyov 40c3152fe8 feat(assistant): сократический / анти-чит режим (фича 3/6)
- тумблер учителя «Сократический режим» (/admin#assistant): для УЧЕНИКОВ
  Квантик объясняет теорию полно, но конкретные задачи не решает «под ключ» —
  даёт метод, первый шаг и наводящий вопрос (assistant_socratic в app_settings)
- авто-анти-чит: явная просьба «сделай за меня / реши моё дз / do my homework»
  включает сократический режим даже без тумблера (_CHEAT_RE)
- учителей/админов и режимы hint/check не ограничивает; работает и в /ask, и в стриме

_socraticFor(role,mode,q) + проброс socratic в buildAskMessages. Бэкенд+админ-UI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:57:24 +03:00
Maxim Dolgolyov 2506a72806 feat(assistant): контекст урока/страницы для Квантика (фича 2/6)
Квантик теперь знает, где находится ученик:
- pageHint() — лёгкий ситуативный контекст («ученик на странице учебник:
  «Физика 7, §12»») подмешивается к ЛЮБОМУ свободному вопросу автоматически
- getPageContext() расширен с учебника на уроки (theory/course/lesson):
  «Объяснить этот урок / Конспект урока / Флешкарты из урока»
- метки чипов адаптируются (параграф/урок)

Бэкенд уже принимал context (pageCtx) — правок не потребовалось.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:52:42 +03:00
Maxim Dolgolyov 089f93b8ee feat(assistant): стриминг ответов Квантика (фича 1/6)
Ответ модели «печатается» вживую через SSE поверх POST (fetch-stream,
не EventSource). Бэкенд: callLLMStream (stream:true, парсинг SSE upstream) +
callLLMStreamFailover (failover только до первого куска) + endpoint
POST /assistant/ask/stream (события meta|delta|done; быстрые пути FAQ/кэш/мета
отдаются одним done). buildAskMessages выделен из askModel (DRY).
Клиент: LS.assistantAskStream (fetch-stream + парсер SSE). Виджет: send()
стримит дельты как plain-текст с CSS-кареткой, на done — KaTeX-рендер,
источники, ссылки, оценка. Фоллбэк на sendNonStream (старый путь) если
стриминг недоступен/упал до первого куска. Cache-Control: no-transform
отключает буферизацию compression.

Проверено против живого шлюза: 24 дельты, первый текст ~1.3с, 100% русский.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:50:11 +03:00
Maxim Dolgolyov 5b4d9324a4 feat(assistant): поддержка keyless-шлюзов + пресет Pollinations
Pollinations (text.pollinations.ai/openai, модель openai) даёт бесплатный
инференс БЕЗ ключа — проверено: 98% чистый русский. Чтобы такой провайдер
считался рабочим (раньше ключ требовался всем, кроме localhost):
- _noKeyNeeded/_aNoKey: localhost ИЛИ pollinations.ai → ключ не обязателен
  (используется в providersOrdered, pingLLM, active-check, testAssistant)
- пресет «Pollinations (без ключа)» в ASSISTANT_PRESETS
- бейдж провайдера: «без ключа» (зелёный) вместо «нет ключа» для keyless

Кейд-провайдеры (Kilo/Gemini/HF/…) по-прежнему требуют ключ — затронуты
только URL с pollinations.ai (спуф в пути отвергается).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:35:56 +03:00
Maxim Dolgolyov d15c15ef2a feat(assistant): сканер бесплатных моделей Kilo в админке
Кнопка «Сканировать модели» в /admin#assistant: тянет live-список со шлюза
провайдера, отбирает бесплатные чат-модели (музыка/картинки/модерация
отсекаются), прогоняет каждую тест-запросом на русском и показывает отчёт
(новые / исчезнувшие / % кириллицы / скорость). «Применить выбранные»
сохраняет список в app_settings (assistant_kilo_models); хардкод KILO_MODELS
остаётся сидом, есть «Вернуть встроенный список».

Backend: scanModels/probeModel/applyModels (admin-only роуты), _kiloModels()
делает список динамическим. Переиспользует _fetchModels. Клиент: adminAssistantScan/Probe/ApplyModels.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:18:04 +03:00