diff --git a/frontend/trainer.html b/frontend/trainer.html
index af7d4c5..c3ae587 100644
--- a/frontend/trainer.html
+++ b/frontend/trainer.html
@@ -622,17 +622,47 @@
// Смешанный текст «проза + математика»: фрагменты в $...$ рендерятся KaTeX
// (через exprToLatex), остальное экранируется. Так формулы видны и в текст-условиях
// (проценты/дроби/verify/choice), где нет единого latex уравнения.
+ // Возврат «косметики» prettyMath к виду, понятному SimExpr-парсеру.
+ function _unpretty(s) {
+ return String(s).replace(/[·×]/g, '*').replace(/−/g, '-').replace(/÷/g, '/').replace(/²/g, '^2').replace(/³/g, '^3');
+ }
+ // Один математический фрагмент → KaTeX-html или null (если не математика / не разобрался).
+ function _mathRun(run) {
+ var suffix = '', core = run, t = core.match(/(%|°)$/);
+ if (t) { suffix = (t[1] === '%') ? '\\%' : '^{\\circ}'; core = core.slice(0, -1); }
+ // рендерим только если есть оператор/степень/дробь/проценты-градусы (одиночные числа/слова — текстом)
+ var worth = /[\/^=<>+\-−·×÷√]|[0-9][A-Za-z]|²|³/.test(run) || suffix;
+ if (!worth) return null;
+ core = _unpretty(core);
+ if (!/[0-9A-Za-z]/.test(core)) return null;
+ var lx = TE.exprToLatex(core);
+ if (lx == null) return null;
+ return kat(lx + suffix, false);
+ }
+ // Авто-рендер математики в прозе: латино-цифровые «прогоны» → KaTeX, кириллица/слова — текст.
+ function _autoProse(s) {
+ var out = '', re = /[0-9A-Za-z(][\w ().,^*\/+\-=<>≤≥≠·×÷√²³°%]*/g, last = 0, m;
+ while ((m = re.exec(s))) {
+ out += esc(s.slice(last, m.index));
+ var run = m[0], tail = '', tm = run.match(/[ ,.;:]+$/);
+ if (tm) { tail = run.slice(run.length - tm[0].length); run = run.slice(0, run.length - tm[0].length); }
+ var r = _mathRun(run);
+ out += (r != null ? r : esc(run)) + esc(tail);
+ last = m.index + m[0].length;
+ }
+ out += esc(s.slice(last));
+ return out;
+ }
+ // Смешанный текст: $...$ — принудительная математика; вне — авто-детект формул.
+ // Безопасно: любой неразобравшийся фрагмент остаётся обычным экранированным текстом.
function renderMixed(text) {
- var parts = String(text == null ? '' : text).split('$');
- var html = '';
+ var parts = String(text == null ? '' : text).split('$'), html = '';
for (var i = 0; i < parts.length; i++) {
- if (i % 2 === 1) { // нечётные сегменты — математика
- // prettyMath уже мог заменить * → ·, − и т.п.; возвращаем к виду для SimExpr-парсера
- var src = parts[i].replace(/[·×]/g, '*').replace(/−/g, '-').replace(/÷/g, '/');
- var lx = TE.exprToLatex(src);
+ if (i % 2 === 1) {
+ var lx = TE.exprToLatex(_unpretty(parts[i]));
html += lx ? (kat(lx, false) || esc(parts[i])) : esc(parts[i]);
} else {
- html += esc(parts[i]);
+ html += _autoProse(parts[i]);
}
}
return html;
@@ -645,8 +675,7 @@
var latex = useFig ? null : problem.latex;
eq.classList.toggle('tr-eq-text', !latex);
if (latex) { setMath(eq, latex, text, true); return; } // целое уравнение (solve/roots/…)
- if (text && text.indexOf('$') !== -1) eq.innerHTML = renderMixed(text); // текст с инлайн-формулами
- else eq.textContent = text;
+ eq.innerHTML = renderMixed(text); // текст-условие: авто-рендер формул в KaTeX
}
// Переключатель «Текст / На чертеже» — только для задач с чертежом и кратким условием.
function renderFigureToggle() {