feat(lab-graph): KaTeX-формулы + панель ввода как редактор формул

«График функции»:
- примеры (чипы) и живой предпросмотр каждой функции рендерятся в KaTeX
  (data-tex на чипах, _initGraphPanel рендерит при открытии)
- предпросмотр теперь всегда виден, крупный и в цвет функции; пустое поле
  показывает плейсхолдер-формулу приглушённо
- НОВОЕ: keypad вставки структур (x², xⁿ, √, a/b, |x|, π, sin/cos/tg/ln/eˣ, ())
  — клик вставляет в активное поле по каретке (как редактор формул в PowerPoint)
- graphInsert(token) с маркером каретки |; активное поле отслеживается по focus

Только фронт. Проверено: node --check, логика вставки 5/5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-24 18:56:31 +03:00
parent ce4f1dcec1
commit 000e42f9b3
3 changed files with 97 additions and 29 deletions
+36 -20
View File
@@ -39,49 +39,65 @@
</div>
<div style="margin-top:8px"></div>
<div class="gp-section-title">Вставить</div>
<div class="gp-keypad">
<button class="kp-btn" data-ins="^2" data-tex="x^2" onclick="graphInsert(this.dataset.ins)"></button>
<button class="kp-btn" data-ins="^" data-tex="x^n" onclick="graphInsert(this.dataset.ins)">xⁿ</button>
<button class="kp-btn" data-ins="sqrt(|)" data-tex="\sqrt{x}" onclick="graphInsert(this.dataset.ins)"></button>
<button class="kp-btn" data-ins="/" data-tex="\tfrac{a}{b}" onclick="graphInsert(this.dataset.ins)">a/b</button>
<button class="kp-btn" data-ins="abs(|)" data-tex="|x|" onclick="graphInsert(this.dataset.ins)">|x|</button>
<button class="kp-btn" data-ins="pi" data-tex="\pi" onclick="graphInsert(this.dataset.ins)">π</button>
<button class="kp-btn" data-ins="sin(|)" data-tex="\sin" onclick="graphInsert(this.dataset.ins)">sin</button>
<button class="kp-btn" data-ins="cos(|)" data-tex="\cos" onclick="graphInsert(this.dataset.ins)">cos</button>
<button class="kp-btn" data-ins="tg(|)" data-tex="\operatorname{tg}" onclick="graphInsert(this.dataset.ins)">tg</button>
<button class="kp-btn" data-ins="ln(|)" data-tex="\ln" onclick="graphInsert(this.dataset.ins)">ln</button>
<button class="kp-btn" data-ins="exp(|)" data-tex="e^x" onclick="graphInsert(this.dataset.ins)"></button>
<button class="kp-btn" data-ins="(|)" data-tex="(\;)" onclick="graphInsert(this.dataset.ins)">( )</button>
</div>
<div class="gp-section-title">Примеры</div>
<div class="gp-preset-group">
<div class="gp-preset-label">Линейные / степенные</div>
<div class="presets-wrap">
<button class="preset-btn" onclick="applyPreset('2x-1')">2x1</button>
<button class="preset-btn" onclick="applyPreset('x^2')"></button>
<button class="preset-btn" onclick="applyPreset('x^2-4')">x²−4</button>
<button class="preset-btn" onclick="applyPreset('x^3-3x')">x³−3x</button>
<button class="preset-btn" onclick="applyPreset('x^4-4x^2+3')">x⁴−4x²+3</button>
<button class="preset-btn" data-tex="2x-1" onclick="applyPreset('2x-1')">2x1</button>
<button class="preset-btn" data-tex="x^2" onclick="applyPreset('x^2')"></button>
<button class="preset-btn" data-tex="x^2-4" onclick="applyPreset('x^2-4')">x²−4</button>
<button class="preset-btn" data-tex="x^3-3x" onclick="applyPreset('x^3-3x')">x³−3x</button>
<button class="preset-btn" data-tex="x^4-4x^2+3" onclick="applyPreset('x^4-4x^2+3')">x⁴−4x²+3</button>
</div>
</div>
<div class="gp-preset-group">
<div class="gp-preset-label">Тригонометрия</div>
<div class="presets-wrap">
<button class="preset-btn" onclick="applyPreset('sin(x)')">sin x</button>
<button class="preset-btn" onclick="applyPreset('cos(x)')">cos x</button>
<button class="preset-btn" onclick="applyPreset('tg(x)')">tg x</button>
<button class="preset-btn" onclick="applyPreset('sin(2x)')">sin 2x</button>
<button class="preset-btn" onclick="applyPreset('x*sin(x)')">x·sin x</button>
<button class="preset-btn" onclick="applyPreset('sin(x)/x')">sin(x)/x</button>
<button class="preset-btn" data-tex="\sin x" onclick="applyPreset('sin(x)')">sin x</button>
<button class="preset-btn" data-tex="\cos x" onclick="applyPreset('cos(x)')">cos x</button>
<button class="preset-btn" data-tex="\operatorname{tg} x" onclick="applyPreset('tg(x)')">tg x</button>
<button class="preset-btn" data-tex="\sin 2x" onclick="applyPreset('sin(2x)')">sin 2x</button>
<button class="preset-btn" data-tex="x\,\sin x" onclick="applyPreset('x*sin(x)')">x·sin x</button>
<button class="preset-btn" data-tex="\tfrac{\sin x}{x}" onclick="applyPreset('sin(x)/x')">sin(x)/x</button>
</div>
</div>
<div class="gp-preset-group">
<div class="gp-preset-label">Показательные / логарифмы</div>
<div class="presets-wrap">
<button class="preset-btn" onclick="applyPreset('exp(x)')"></button>
<button class="preset-btn" onclick="applyPreset('2^x')"></button>
<button class="preset-btn" onclick="applyPreset('ln(x)')">ln x</button>
<button class="preset-btn" onclick="applyPreset('log(x)')">log x</button>
<button class="preset-btn" data-tex="e^x" onclick="applyPreset('exp(x)')"></button>
<button class="preset-btn" data-tex="2^x" onclick="applyPreset('2^x')"></button>
<button class="preset-btn" data-tex="\ln x" onclick="applyPreset('ln(x)')">ln x</button>
<button class="preset-btn" data-tex="\log x" onclick="applyPreset('log(x)')">log x</button>
</div>
</div>
<div class="gp-preset-group">
<div class="gp-preset-label">Прочие</div>
<div class="presets-wrap">
<button class="preset-btn" onclick="applyPreset('sqrt(x)')">√x</button>
<button class="preset-btn" onclick="applyPreset('1/x')">1/x</button>
<button class="preset-btn" onclick="applyPreset('abs(x)')">|x|</button>
<button class="preset-btn" onclick="applyPreset('floor(x)')">⌊x⌋</button>
<button class="preset-btn" onclick="applyPreset('1/(1+exp(-x))')">σ(x)</button>
<button class="preset-btn" data-tex="\sqrt{x}" onclick="applyPreset('sqrt(x)')">√x</button>
<button class="preset-btn" data-tex="\tfrac{1}{x}" onclick="applyPreset('1/x')">1/x</button>
<button class="preset-btn" data-tex="|x|" onclick="applyPreset('abs(x)')">|x|</button>
<button class="preset-btn" data-tex="\lfloor x \rfloor" onclick="applyPreset('floor(x)')">⌊x⌋</button>
<button class="preset-btn" data-tex="\sigma(x)" onclick="applyPreset('1/(1+exp(-x))')">σ(x)</button>
</div>
</div>