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() {