From ff9900bdcc575cfb2fa93121fccc35b17323112c Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 26 Jun 2026 12:23:03 +0300 Subject: [PATCH] =?UTF-8?q?feat(trainer):=20=D0=B3=D0=B5=D0=BE=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D1=80=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=B5=20=D1=87?= =?UTF-8?q?=D0=B5=D1=80=D1=82=D0=B5=D0=B6=D0=B8=20=D0=B7=D0=B0=D0=B4=D0=B0?= =?UTF-8?q?=D1=87=20=E2=80=94=20=D0=B4=D0=B2=D0=B8=D0=B6=D0=BE=D0=BA=20?= =?UTF-8?q?=D1=84=D0=B8=D0=B3=D1=83=D1=80=20+=20=D0=B8=D0=BB=D0=BB=D1=8E?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B2=D0=BE=20?= =?UTF-8?q?=D0=B2=D1=81=D0=B5=D1=85=20=D0=B3=D0=B5=D0=BE=D0=BC.=20=D1=82?= =?UTF-8?q?=D0=B5=D0=BC=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TrainerFigures (frontend/js/trainer/figures.js) — безопасный SVG-рендер «фигуры как данные» (модель SimForge): 11 типов — прямоугольный треугольник, углы треугольника/смежные/внешний, прямоугольник, квадрат, треугольник по основанию и высоте, трапеция, параллелограмм, ромб, правильный n-угольник, подобные треугольники. Чертёж строится из чисел (params), ⛔ без eval/Function, подписи экранируются, искомая величина — «?». Белые штрихи под индиго-сцену. - generators.js: figure-спека на всех 15 геом-генераторах (Углы, Пифагор, Площади, Многоугольники, Подобие) — привязка размеров к параметрам задачи. - _trainer_engine.js: figure прокидывается в problem. - trainer.html: контейнер #tr-figure в шапке-герое, renderFigure() в newProblem, скрыт для текстовых задач, скрипт-тег, CSS. Верификация: headless-смоук 5489 проверок / 900 рендеров (нет NaN/ + @@ -586,6 +592,14 @@ var h = kat(latex, display); if (h) el.innerHTML = h; else el.textContent = fallbackText; } + // Чертёж задачи (геометрия): TrainerFigures строит безопасный SVG по cur.figure + params. + function renderFigure(problem) { + var box = $('tr-figure'); if (!box) return; + var svg = (problem && problem.figure && window.TrainerFigures) + ? window.TrainerFigures.render(problem.figure, problem.params) : null; + if (svg) { box.innerHTML = svg; box.style.display = ''; } + else { box.innerHTML = ''; box.style.display = 'none'; } + } var topics = (TG.topics ? TG.topics() : [{ key: null, label: 'Задачи' }]).concat([{ key: 'word', label: 'Текстовые задачи', word: true }]); var isTeacher = !!(ip && ip.isTeacher); @@ -626,6 +640,7 @@ } function serveWordProblem() { var eq = $('tr-eq'); eq.classList.add('tr-eq-text'); + renderFigure(null); // текстовые задачи — без чертежа $('tr-solution').style.display = 'none'; $('tr-solution').innerHTML = ''; var fb = $('tr-feedback'); fb.className = 'tr-feedback'; fb.textContent = ''; if (!wordPool.length) { @@ -929,6 +944,7 @@ var eq = $('tr-eq'); eq.classList.toggle('tr-eq-text', !cur.latex); // текстовый prompt (проценты/упрощение) — другим шрифтом setMath(eq, cur.latex, cur.display, true); + renderFigure(cur); // чертёж для геометрии (иначе скрыт) applyInputMode(); var inp = $('tr-input'); inp.value = ''; inp.disabled = false;