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>
This commit is contained in:
Maxim Dolgolyov
2026-06-25 15:59:11 +03:00
parent d5587b4eb1
commit 6eaf68a158
3 changed files with 210 additions and 12 deletions
+44 -4
View File
@@ -39,9 +39,16 @@
}
.tr-wrap { max-width: 740px; margin: 0 auto; padding: 34px 20px 90px; }
@keyframes trUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.tr-head, .tr-overall, .tr-mode, .tr-topbar, .tr-skillpanel, .tr-card { animation: trUp .5s var(--ease) both; }
.tr-head, .tr-overall, .tr-mode, .tr-subjects, .tr-topbar, .tr-skillpanel, .tr-card { animation: trUp .5s var(--ease) both; }
.tr-overall { animation-delay: .04s; } .tr-mode { animation-delay: .06s; }
.tr-topbar { animation-delay: .1s; } .tr-skillpanel { animation-delay: .13s; } .tr-card { animation-delay: .16s; }
.tr-subjects { animation-delay: .08s; } .tr-topbar { animation-delay: .1s; } .tr-skillpanel { animation-delay: .13s; } .tr-card { animation-delay: .16s; }
/* ── фильтр по предмету (Алгебра / Геометрия) ── */
.tr-subjects { display: flex; gap: 7px; margin-bottom: 14px; }
.tr-subjects:empty { display: none; }
.tr-subbtn { font: inherit; font-size: .86rem; font-weight: 800; cursor: pointer; padding: 8px 18px; border-radius: 99px; border: 1px solid rgba(99,102,241,.2); background: #fff; color: var(--ink-soft); transition: .16s var(--ease); }
.tr-subbtn:hover { border-color: var(--g1); color: var(--accent-ink); }
.tr-subbtn.on { color: #fff; border-color: transparent; background: linear-gradient(135deg, var(--g1), var(--g2)); box-shadow: 0 8px 20px rgba(99,102,241,.3); }
.tr-head { margin-bottom: 20px; }
.tr-h1 {
@@ -335,6 +342,7 @@
</div>
</div>
<div class="tr-subjects" id="tr-subjects"></div>
<div class="tr-topbar">
<span class="tr-nav-eyebrow">Тема</span>
<div class="tr-topics" id="tr-topics"></div>
@@ -470,6 +478,7 @@
var topics = (TG.topics ? TG.topics() : [{ key: null, label: 'Задачи' }]).concat([{ key: 'word', label: 'Текстовые задачи', word: true }]);
var isTeacher = !!(ip && ip.isTeacher);
var isAdmin = !!(ip && ip.isAdmin);
var curSubject = 'algebra'; // фильтр предмета (Алгебра/Геометрия)
var customGens = []; // пользовательские генераторы (P13), тема «Авторские»
function skillKey(g) { return g.skill || g.id; }
function skillsOf(topicKey) {
@@ -561,13 +570,29 @@
return '';
}
function topicVisible(t) { return !!(t && (t.word || t.custom || (t.subject || 'algebra') === curSubject)); }
function renderTopics() {
$('tr-topics').innerHTML = topics.map(function (t, i) {
if (!topicVisible(t)) return '';
var done = topicMastered(t.key) ? '<span class="tr-badge" title="Тема освоена">' + ICON.star + '</span>' : '';
var gr = t.grade ? '<span class="tr-grade" title="' + t.grade + ' класс">' + t.grade + '</span>' : '';
return '<button class="tr-chip' + (t.key === curTopic ? ' on' : '') + '" type="button" data-ti="' + i + '">' + esc(t.label) + gr + done + '</button>';
}).join('');
}
function presentSubjects() {
var seen = {}, out = [];
topics.forEach(function (t) { if (t.subject && !seen[t.subject]) { seen[t.subject] = 1; out.push(t.subject); } });
return out;
}
function renderSubjects() {
var el = $('tr-subjects'); if (!el) return;
var subs = presentSubjects();
if (subs.length <= 1) { el.innerHTML = ''; return; }
var LBL = { algebra: 'Алгебра', geometry: 'Геометрия' };
el.innerHTML = subs.map(function (s) {
return '<button class="tr-subbtn' + (s === curSubject ? ' on' : '') + '" type="button" data-sub="' + s + '">' + esc(LBL[s] || s) + '</button>';
}).join('');
}
function skillPanelHeader() {
var hd = $('tr-skillpanel-hd'); if (!hd) return;
var t = topics.filter(function (x) { return x.key === curTopic; })[0];
@@ -778,7 +803,7 @@
var last = (lastSkill !== undefined) ? lastSkill : (curGen ? skillKey(curGen) : null);
var id = TA.nextSkill({ ordered: ordered, progress: prog, queue: reviewQ, answered: sessAnswered, last: last });
var g = id ? gens.filter(function (x) { return skillKey(x) === id; })[0] : null;
if (g) { curGen = g; curTopic = g.topic; renderTopics(); renderSkills(); }
if (g) { curGen = g; curTopic = g.topic; if (g.subject) curSubject = g.subject; renderSubjects(); renderTopics(); renderSkills(); }
}
function recordAnswer(correct) {
var sk = currentSkill();
@@ -983,6 +1008,20 @@
$('tr-teacher').addEventListener('click', function (e) { if (e.target === $('tr-teacher')) $('tr-teacher').style.display = 'none'; });
$('tr-analytics-btn').addEventListener('click', openAnalytics);
$('tr-builder-btn').addEventListener('click', function () { location.href = '/trainer-builder'; });
$('tr-subjects').addEventListener('click', function (e) {
var b = e.target.closest('.tr-subbtn'); if (!b) return;
curSubject = b.getAttribute('data-sub');
renderSubjects();
var ct = topics.filter(function (x) { return x.key === curTopic; })[0];
if (ct && topicVisible(ct)) { renderTopics(); return; } // текущая тема видна — просто перерисовать
var first = topics.filter(function (x) { return (x.subject || 'algebra') === curSubject; })[0];
if (first) {
curTopic = first.key;
var ss = skillsOf(curTopic); curGen = ss[0] || curGen;
for (var i = 0; i < ss.length; i++) { var p = prog[skillKey(ss[i])]; if (!(p && p.mastered)) { curGen = ss[i]; break; } }
}
renderTopics(); renderSkills(); newProblem();
});
$('tr-an-close').addEventListener('click', function () { $('tr-analytics').style.display = 'none'; });
$('tr-analytics').addEventListener('click', function (e) { if (e.target === $('tr-analytics')) $('tr-analytics').style.display = 'none'; });
$('tr-an-body').addEventListener('click', function (e) {
@@ -993,6 +1032,7 @@
var b = e.target.closest('.tr-chip'); if (!b) return;
var t = topics[+b.getAttribute('data-ti')]; if (!t) return;
curTopic = t.key;
if (t.subject) { curSubject = t.subject; renderSubjects(); }
renderTopics();
if (t.word) { renderSkills(); loadWordPool(function () { serveWordProblem(); }); return; }
var ss = skillsOf(curTopic);
@@ -1051,7 +1091,7 @@
curGen = ss[0] || gens[0];
for (var si = 0; si < ss.length; si++) { var p = prog[skillKey(ss[si])]; if (!(p && p.mastered)) { curGen = ss[si]; break; } }
if (smart) pickNext(null); // адаптивный первый навык (last=null — можно взять текущий)
renderTopics(); renderSkills(); updateSession(); updateOverall(); newProblem();
renderSubjects(); renderTopics(); renderSkills(); updateSession(); updateOverall(); newProblem();
if (isTeacher) $('tr-analytics-btn').style.display = '';
if (isAdmin) $('tr-builder-btn').style.display = '';
}