From 718772a2aa4b7be607094dc1c99101ca35584428 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Wed, 27 May 2026 13:04:37 +0300 Subject: [PATCH] =?UTF-8?q?feat(textbooks):=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B0=D1=82=D1=8C=20'=D0=A1=D0=B2=D1=8F?= =?UTF-8?q?=D0=B7=D0=BA=D0=B0=20x=C2=B2=20=E2=86=94=20=E2=88=9Ax'=20=D0=B2?= =?UTF-8?q?=20=D0=BD=D0=B0=D0=B3=D0=BB=D1=8F=D0=B4=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D0=B9=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Было: два изолированных блока (квадрат и линия), связь неявная. Стало: конвейер из трёх шагов со стрелками: [x] →(возвести в квадрат)→ [x² с площадью квадрата] →(извлечь корень)→ [|x|] Ключевое улучшение: ползунок теперь от -8 до +8. При отрицательном x: - площадь всё равно положительная (x²) - корень даёт |x|, не x - формула снизу подсвечивается янтарным предупреждением 'это |x| ≠ x' Под конвейером: живая формула KaTeX типа 'x = 3 → x² = 9 → √9 = 3 ✓'. При отрицательном x текст явно показывает: 'x = -3 → x² = 9 → √9 = 3 ≠ -3 → это |x| = 3'. Мобайл: вертикальная компоновка со стрелками вниз. --- frontend/textbooks/algebra_8.html | 108 +++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 23 deletions(-) diff --git a/frontend/textbooks/algebra_8.html b/frontend/textbooks/algebra_8.html index e04ce8a..0606b55 100644 --- a/frontend/textbooks/algebra_8.html +++ b/frontend/textbooks/algebra_8.html @@ -454,6 +454,28 @@ input,select,textarea{font-family:inherit} /* hover-preview карточек выключен — мешал перекрытием соседних рядов */ .psel-card-preview{display:none!important} +/* Конвейер x → x² → √(x²) в §1 */ +.dual-pipeline{display:flex;align-items:center;justify-content:space-between;gap:6px;flex-wrap:wrap;margin-top:14px;padding:14px 8px;background:var(--card);border-radius:12px;border:1px solid var(--border)} +.dual-step{flex:1;min-width:90px;text-align:center;padding:10px 8px;border-radius:10px;background:rgba(233,30,99,0.04);border:1.5px solid var(--border)} +.dual-step.dual-input{background:rgba(233,30,99,0.12);border-color:rgba(233,30,99,0.4)} +.dual-step.dual-square{background:rgba(155,93,229,0.10);border-color:rgba(155,93,229,0.35)} +.dual-step.dual-output{background:rgba(3,169,244,0.12);border-color:rgba(3,169,244,0.4)} +.dual-step-lab{font-size:.66rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin-bottom:4px} +.dual-step-val{font-size:1.8rem;font-weight:900;color:var(--text);font-family:'JetBrains Mono',monospace;line-height:1} +.dual-step-val.small{font-size:1.3rem;margin-top:4px} +.dual-step-cap{font-size:.78rem;color:var(--muted);margin-top:4px;font-family:'JetBrains Mono',monospace} +.dual-arrow{flex:0 0 auto;display:flex;flex-direction:column;align-items:center;min-width:60px} +.dual-arrow svg{width:54px;height:24px} +.dual-arrow-lab{font-size:.68rem;color:var(--pri2);font-weight:600;margin-top:2px;text-align:center;line-height:1.2} +.dual-formula{margin-top:14px;padding:10px 14px;background:linear-gradient(135deg,var(--pri-soft),var(--acc-soft));border-radius:9px;text-align:center;font-size:.95rem;line-height:1.7;border:1px solid var(--border)} +.dual-formula.mod-active{background:linear-gradient(135deg,#fef3c7,#fce7f3);border-color:var(--warn)} +@media(max-width:680px){ + .dual-pipeline{flex-direction:column;gap:8px} + .dual-step{width:100%} + .dual-arrow{flex-direction:row;gap:8px} + .dual-arrow svg{transform:rotate(90deg);width:24px;height:54px} +} + /* Task 8: section fade transitions */ .sec.fade-out{animation:secFadeOut .18s ease forwards} .sec.fade-in{animation:secFadeIn .22s ease forwards} @@ -1697,25 +1719,40 @@ function buildP1(){ `)} - ${widget('Связка x² ↔ √x', 'VISUAL', 'Слева — площадь квадрата (x²). Справа — длина стороны (√x). Меняйте x ползунком — оба зеркалят друг друга.', ` -
+ ${widget('Конвейер: x → x² → √(x²)', 'VISUAL', 'Двигайте ползунок. Слева — возведение в квадрат, справа — извлечение корня. Попробуйте отрицательное x — результат всё равно положительный!', ` +
x = - - 5.0 + + 3.0
-
-
-
x²: квадрат со стороной x
- -
S = 25.0
+
+
+
Вход
+
3
+
x
-
-
√(x²): сторона возвращается
- -
сторона = 5.0
+
+ +
возвести в квадрат
+
+
+
Площадь
+ +
9
+
x² = $x \\cdot x$
+
+
+ +
извлечь корень
+
+
+
Выход
+
3
+
$\\sqrt{x^2} = |x|$
-

√(x²) = |x| — корень и квадрат «отменяют» друг друга для неотрицательных чисел.

+
$x = 3$ → $x^2 = 9$ → $\\sqrt{9} = 3$ ✓ (вернулись к исходному)
+

При отрицательном x результат всё равно положительный — это и есть смысл $\\sqrt{x^2} = |x|$

`)} ${makeCard('home','Домашнее задание','1.11–1.15',` @@ -2059,19 +2096,44 @@ function initDual(){ const x = document.getElementById('dual-x'); if(!x) return; const xv = document.getElementById('dual-x-val'); + const inEl = document.getElementById('dual-in'); const sq = document.getElementById('dual-sq'); const area = document.getElementById('dual-area'); - const side = document.getElementById('dual-side'); + const out = document.getElementById('dual-out'); + const formula = document.getElementById('dual-formula'); + function fmt(n){ return Number.isInteger(n) ? String(n) : n.toFixed(1); } function upd(){ const v = +x.value; - xv.textContent = v.toFixed(1); - const sz = Math.min(120, v * 10); - sq.setAttribute('x', 80 - sz/2); - sq.setAttribute('y', 80 - sz/2); - sq.setAttribute('width', Math.max(2, sz)); - sq.setAttribute('height', Math.max(2, sz)); - area.textContent = (v*v).toFixed(1); - side.textContent = v.toFixed(1); + const sq2 = v * v; + const root = Math.sqrt(sq2); + xv.textContent = fmt(v); + if(inEl) inEl.textContent = fmt(v); + // Размер квадрата: |v|·8, max 60 + const sz = Math.min(60, Math.abs(v) * 8 + 2); + if(sq){ + sq.setAttribute('x', 40 - sz/2); + sq.setAttribute('y', 40 - sz/2); + sq.setAttribute('width', sz); + sq.setAttribute('height', sz); + } + if(area) area.textContent = fmt(sq2); + if(out) out.textContent = fmt(root); + // Подсветка: если v < 0 — показать что вернулось |v|, а не v + if(formula){ + if(v < 0){ + formula.innerHTML = `$x = ${fmt(v)}$ → $x^2 = ${fmt(sq2)}$ → $\\sqrt{${fmt(sq2)}} = ${fmt(root)}$ ≠ ${fmt(v)} → это |x| = ${fmt(root)}`; + formula.classList.add('mod-active'); + } else if(v === 0){ + formula.innerHTML = `$x = 0$ → $x^2 = 0$ → $\\sqrt{0} = 0$ — особый случай`; + formula.classList.remove('mod-active'); + } else { + formula.innerHTML = `$x = ${fmt(v)}$ → $x^2 = ${fmt(sq2)}$ → $\\sqrt{${fmt(sq2)}} = ${fmt(root)}$ ✓ (вернулись к исходному)`; + formula.classList.remove('mod-active'); + } + if(window.renderMathInElement){ + try{ renderMathInElement(formula, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}],throwOnError:false}); }catch(e){} + } + } } x.addEventListener('input', upd); upd();