feat(trainer): P13 — конструктор параметрических генераторов
- custom_generators (мигр.084, spec_json + draft/published); customGeneratorController: validateGenSpec без исполнения (лимиты/типы), CRUD own+published + ownership - /api/practice/generators[/:id]; клиент LS.practiceGen* - страница /trainer-builder (учитель): форма (pick/derive/lhs/rhs/display/answer/solution) + живое превью через TE.instantiate(strict) (материализация + проверка ответа подстановкой) + список своих (правка/удаление/публикация) - тренажёр грузит свои+опубликованные генераторы в тему «Мои генераторы» (пошаговый режим работает); пункт сайдбара /trainer-builder (teacher-only) - тесты custom-generators.test.js 12/12; смоук движка 402/402 (T17 кастомный спек + strict-валидация); страница 33/33; ROADMAP_V2 P13 → DONE Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+17
-5
@@ -428,8 +428,12 @@
|
||||
|
||||
var topics = (TG.topics ? TG.topics() : [{ key: null, label: 'Задачи' }]).concat([{ key: 'word', label: 'Текстовые задачи', word: true }]);
|
||||
var isTeacher = !!(ip && ip.isTeacher);
|
||||
var customGens = []; // пользовательские генераторы (P13), тема «Мои генераторы»
|
||||
function skillKey(g) { return g.skill || g.id; }
|
||||
function skillsOf(topicKey) { return TG.byTopic ? TG.byTopic(topicKey) : gens; }
|
||||
function skillsOf(topicKey) {
|
||||
if (topicKey === 'custom') return customGens;
|
||||
return TG.byTopic ? TG.byTopic(topicKey) : gens;
|
||||
}
|
||||
function isWord() { return curTopic === 'word'; }
|
||||
function currentSkill() { return (cur && cur.kind === 'word') ? (cur.skill || 'word-linear') : skillKey(curGen); }
|
||||
|
||||
@@ -998,10 +1002,18 @@
|
||||
renderTopics(); renderSkills(); updateSession(); updateOverall(); newProblem();
|
||||
if (isTeacher) $('tr-analytics-btn').style.display = '';
|
||||
}
|
||||
(LS.practiceProgressList ? LS.practiceProgressList() : Promise.resolve(null))
|
||||
.then(function (r) { if (r && r.progress) r.progress.forEach(function (row) { prog[row.skill] = row; }); })
|
||||
.catch(function () {})
|
||||
.then(boot);
|
||||
Promise.all([
|
||||
LS.practiceProgressList ? LS.practiceProgressList().catch(function () { return null; }) : Promise.resolve(null),
|
||||
LS.practiceGenList ? LS.practiceGenList().catch(function () { return null; }) : Promise.resolve(null)
|
||||
]).then(function (res) {
|
||||
var pr = res[0], cgr = res[1];
|
||||
if (pr && pr.progress) pr.progress.forEach(function (row) { prog[row.skill] = row; });
|
||||
if (cgr && cgr.generators && cgr.generators.length) {
|
||||
customGens = cgr.generators;
|
||||
topics.push({ key: 'custom', label: 'Мои генераторы', custom: true });
|
||||
}
|
||||
boot();
|
||||
}).catch(boot);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user