feat(labs): Фаза1 — фреймворк учебных заданий (LabTasks)

Превращает песочницы в учебные инструменты: задание → ответ числом с допуском →
проверка/подсказка/прогресс (по образцу race.js, но переиспользуемо).
- _tasks.js: LabTasks (панель, прогресс-точки, проверка с tol, KaTeX в условии).
- Интеграция в loadTheory (одна точка): панель «Задания» дописывается в теорию,
  бейдж на кнопке теории когда задания есть.
- Данные на 5 симуляций: quadratic, trigcircle, normaldist, projectile, pendulum.
Проверка на клиенте (учебные, не оценочные). XP — отдельным инкрементом.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-13 10:42:17 +03:00
parent 28db2de74f
commit 15282c50b3
3 changed files with 168 additions and 3 deletions
+12 -3
View File
@@ -242,7 +242,15 @@ const SIMS = [
const _rm = window.LabRegistry ? window.LabRegistry.get(simId) : null;
const t = (_rm && _rm.theory) ? _rm.theory : THEORY[simId];
const el = document.getElementById('theory-content');
if (!t) { el.innerHTML = '<div class="tp-text" style="text-align:center;padding:40px 0;color:var(--text-3)">Теория для этой симуляции пока не добавлена</div>'; return; }
// Фаза 1: панель учебных заданий (если есть для этой симуляции)
const tasksHtml = (window.LabTasks && LabTasks.has(simId)) ? LabTasks.mountHtml(simId) : '';
const tbtn = document.getElementById('theory-toggle');
if (tbtn) tbtn.classList.toggle('lt-has', !!tasksHtml);
if (!t) {
el.innerHTML = '<div class="tp-text" style="text-align:center;padding:40px 0;color:var(--text-3)">Теория для этой симуляции пока не добавлена</div>' + tasksHtml;
if (tasksHtml) LabTasks.afterMount(simId);
return;
}
let html = `<div class="tp-title">${LS.icon('book-open',16)} ${t.title}</div>`;
for (const s of t.sections) {
html += '<div class="tp-section">';
@@ -252,12 +260,13 @@ const SIMS = [
if (s.vars) html += `<div class="tp-var-list">${s.vars.map(([v,d]) => `<div class="tp-var"><b>${v}</b> — ${d}</div>`).join('')}</div>`;
html += '</div>';
}
el.innerHTML = html;
// render KaTeX formulas
el.innerHTML = html + tasksHtml;
// render KaTeX formulas (теория)
el.querySelectorAll('.tp-formula[data-formula]').forEach(div => {
try { katex.render(div.dataset.formula, div, { displayMode: true, throwOnError: false }); }
catch(e) { div.textContent = div.dataset.formula; }
});
if (tasksHtml) LabTasks.afterMount(simId);
}
/* ── Контент-движок, Фаза 5: чип «Связано с программой» ──────────────────