#!/usr/bin/env node 'use strict'; /** * Phase 0 skeleton generator for Алгебра 9 chapter files. * Produces: algebra_9_ch1.html ... ch4.html as functioning skeletons * with stub bodies for each section. Phase 1+ will fill in the content. */ const fs = require('fs'); const path = require('path'); const OUT_DIR = path.join(__dirname, '..', '..', 'frontend', 'textbooks'); /* ===== Chapter data ===== */ const CHAPTERS = [ { chN: 1, title: 'Рациональные выражения', sub: 'Рациональные дроби · ОДЗ · действия с дробями', heroH2: 'Рациональные выражения — алгебра дробей', heroP: 'Здесь мы изучаем рациональные дроби (выражения вида $\\dfrac{P(x)}{Q(x)}$), их область допустимых значений, основное свойство и сокращение, четыре арифметических действия и преобразование сложных рациональных выражений.', palette: { pri:'#d97706', pri2:'#b45309', priSoft:'#fef3c7', acc:'#f59e0b', acc2:'#d97706', accSoft:'#fef9c3', hdrGrad:'linear-gradient(110deg,#92400e 0%,#d97706 55%,#fbbf24 100%)', hdrShadow:'rgba(251,191,36,.2)', hdrWmStroke:'rgba(255,235,180,.12)', darkBg:'#0a0a0e', darkCard:'#13120a', darkCardSoft:'#18160a', darkText:'#fef9e7', darkMuted:'#a39070', darkBorder:'#2a2512', confetti:['#d97706','#f59e0b','#fbbf24','#10b981','#0891b2'], heroWm:'A/B', }, paras: [ { id:'p1', num:'§ 1', name:'Рациональная дробь', sub:'ОДЗ выражения', watermark:'P/Q', secAcc:'#d97706', secAccD:'#b45309', secAccSoft:'#fef3c7' }, { id:'p2', num:'§ 2', name:'Основное свойство дроби', sub:'Сокращение', watermark:'k', secAcc:'#f59e0b', secAccD:'#d97706', secAccSoft:'#fef9c3' }, { id:'p3', num:'§ 3', name:'Сложение и вычитание', sub:'Общий знаменатель', watermark:'+', secAcc:'#059669', secAccD:'#047857', secAccSoft:'#d1fae5' }, { id:'p4', num:'§ 4', name:'Умножение и деление', sub:'×, ÷ дробей', watermark:'×', secAcc:'#7c3aed', secAccD:'#6d28d9', secAccSoft:'#ede9fe' }, { id:'p5', num:'§ 5', name:'Преобразование выражений', sub:'Сложные дроби', watermark:'…', secAcc:'#db2777', secAccD:'#9d174d', secAccSoft:'#fce7f3' }, ], achLabels: { start:'Начало главы 1!', p2_done:'Сокращение дробей освоено!', p4_done:'Действия с дробями освоены!', p5_done:'Преобразование выражений освоено!', ch1_done:'Глава 1 пройдена!', }, tips: [ { sec:'p1', html:'ОДЗ — это значения, при которых знаменатель $\\ne 0$. Всегда выписывай ОДЗ перед работой с дробью.' }, { sec:'p2', html:'Сокращение возможно после разложения на множители числителя и знаменателя.' }, { sec:'p3', html:'Для сложения дробей с разными знаменателями ищи наименьший общий знаменатель.' }, { sec:'p4', html:'$\\dfrac{a}{b} \\cdot \\dfrac{c}{d} = \\dfrac{ac}{bd}$, $\\dfrac{a}{b} : \\dfrac{c}{d} = \\dfrac{ad}{bc}$.' }, { sec:'p5', html:'Сложные выражения упрощай по действиям, не забывай об ОДЗ.' }, { sec:'final1', html:'5 боссов главы 1. Удачи!' }, ], sidebars: { p1:[ ['Дробь','$\\dfrac{P(x)}{Q(x)}$, где $P, Q$ — многочлены'], ['ОДЗ','$Q(x) \\ne 0$'], ['Целое','частный случай при $Q = 1$'] ], p2:[ ['Свойство','$\\dfrac{P \\cdot R}{Q \\cdot R} = \\dfrac{P}{Q}$ при $R \\ne 0$'], ['Сокращение','делим числитель и знаменатель на общий множитель'], ['Знак','$\\dfrac{-a}{-b} = \\dfrac{a}{b}$, $\\dfrac{-a}{b} = -\\dfrac{a}{b}$'] ], p3:[ ['Одинак.знам.','$\\dfrac{a}{c} \\pm \\dfrac{b}{c} = \\dfrac{a \\pm b}{c}$'], ['Разные знам.','приведи к общему знаменателю'], ['НОЗ','наименьший общий знаменатель'] ], p4:[ ['Умножение','$\\dfrac{a}{b} \\cdot \\dfrac{c}{d} = \\dfrac{ac}{bd}$'], ['Деление','$\\dfrac{a}{b} : \\dfrac{c}{d} = \\dfrac{a}{b} \\cdot \\dfrac{d}{c}$'], ['Степень','$\\left(\\dfrac{a}{b}\\right)^n = \\dfrac{a^n}{b^n}$'] ], p5:[ ['Шаг 1','выпиши ОДЗ'], ['Шаг 2','разложи на множители'], ['Шаг 3','выполни действия по порядку'], ['Шаг 4','сократи результат'] ], final1:[ ['§§1–5','теория главы 1'], ['Боссов','5'], ['Награда','+100 XP'] ], }, }, { chN: 2, title: 'Функции', sub: 'Числовой аргумент · свойства · чётность · сдвиги', heroH2: 'Функции — изучаем поведение и графики', heroP: 'Здесь мы знакомимся с функцией числового аргумента: область определения $D(f)$, область значений $E(f)$, возрастание/убывание, нули, наибольшее и наименьшее значения, чётность и сдвиги графиков $y = f(x) + b$, $y = f(x \\pm a)$.', palette: { pri:'#059669', pri2:'#047857', priSoft:'#d1fae5', acc:'#10b981', acc2:'#059669', accSoft:'#ecfdf5', hdrGrad:'linear-gradient(110deg,#064e3b 0%,#059669 55%,#34d399 100%)', hdrShadow:'rgba(167,243,208,.2)', hdrWmStroke:'rgba(209,250,229,.12)', darkBg:'#021410', darkCard:'#0a1f1a', darkCardSoft:'#0d2620', darkText:'#e0fcf3', darkMuted:'#7aa896', darkBorder:'#163d2f', confetti:['#059669','#10b981','#34d399','#f59e0b','#0891b2'], heroWm:'f(x)', }, paras: [ { id:'p6', num:'§ 6', name:'Функция числового аргумента', sub:'$D(f)$, $E(f)$', watermark:'D/E', secAcc:'#059669', secAccD:'#047857', secAccSoft:'#d1fae5' }, { id:'p7', num:'§ 7', name:'Свойства функции', sub:'нули, монотонность, экстр.', watermark:'↗', secAcc:'#10b981', secAccD:'#059669', secAccSoft:'#ecfdf5' }, { id:'p8', num:'§ 8', name:'Чётные и нечётные функции', sub:'симметрия графика', watermark:'±', secAcc:'#0891b2', secAccD:'#0e7490', secAccSoft:'#cffafe' }, { id:'p9', num:'§ 9', name:'Сдвиги графиков', sub:'$y=f(x)+b$, $y=f(x \\pm a)$', watermark:'→', secAcc:'#7c3aed', secAccD:'#6d28d9', secAccSoft:'#ede9fe' }, ], achLabels: { start:'Начало главы 2!', p7_done:'Свойства функции освоены!', p8_done:'Чётность освоена!', p9_done:'Сдвиги графиков освоены!', ch2_done:'Глава 2 пройдена!', }, tips: [ { sec:'p6', html:'Функция — это правило: каждому $x$ из $D(f)$ соответствует ровно одно $y$.' }, { sec:'p7', html:'Нули функции — это решения уравнения $f(x) = 0$.' }, { sec:'p8', html:'Чётная функция: $f(-x) = f(x)$. Нечётная: $f(-x) = -f(x)$.' }, { sec:'p9', html:'$y = f(x) + b$ — сдвиг по $Oy$. $y = f(x - a)$ — сдвиг по $Ox$ вправо на $a$.' }, { sec:'final2', html:'4 босса главы 2.' }, ], sidebars: { p6:[ ['Функция','правило $x \\to y$'], ['$D(f)$','область определения'], ['$E(f)$','область значений'] ], p7:[ ['Нуль','$f(x_0) = 0$'], ['Возрастает','при бо́льшем $x$ — бо́льшее $f(x)$'], ['Убывает','при бо́льшем $x$ — меньшее $f(x)$'], ['$y_{max}$','наиб. значение на промежутке'] ], p8:[ ['Чётная','$f(-x) = f(x)$ — симм. отн. $Oy$'], ['Нечётная','$f(-x) = -f(x)$ — симм. отн. $O$'], ['Ни та, ни др.','общий случай'] ], p9:[ ['$f(x) + b$','сдвиг вверх на $b$'], ['$f(x) - b$','сдвиг вниз на $b$'], ['$f(x - a)$','сдвиг вправо на $a$'], ['$f(x + a)$','сдвиг влево на $a$'] ], final2:[ ['§§6–9','теория главы 2'], ['Боссов','4'], ['Награда','+100 XP'] ], }, }, { chN: 3, title: 'Дробно-рациональные уравнения и неравенства', sub: 'Уравнения · системы · окружность · метод интервалов', heroH2: 'Дробно-рациональные уравнения и неравенства', heroP: 'Здесь мы изучаем дробно-рациональные уравнения, системы нелинейных уравнений (включая графический способ), длину отрезка и уравнение окружности $(x-a)^2 + (y-b)^2 = r^2$, а также метод интервалов для дробно-рациональных неравенств.', palette: { pri:'#7c3aed', pri2:'#6d28d9', priSoft:'#ede9fe', acc:'#a78bfa', acc2:'#7c3aed', accSoft:'#f5f3ff', hdrGrad:'linear-gradient(110deg,#3b0764 0%,#7c3aed 55%,#a78bfa 100%)', hdrShadow:'rgba(196,181,253,.2)', hdrWmStroke:'rgba(237,233,254,.12)', darkBg:'#0d0418', darkCard:'#1a0d2a', darkCardSoft:'#1f1130', darkText:'#f3e8ff', darkMuted:'#a08fb5', darkBorder:'#3a1f54', confetti:['#7c3aed','#a78bfa','#c4b5fd','#f59e0b','#0891b2'], heroWm:'≠0', }, paras: [ { id:'p10', num:'§ 10', name:'Дробно-рациональные уравнения', sub:'$\\dfrac{P}{Q} = 0$', watermark:'=0', secAcc:'#7c3aed', secAccD:'#6d28d9', secAccSoft:'#ede9fe' }, { id:'p11', num:'§ 11', name:'Системы нелинейных уравнений', sub:'подстановка · графика', watermark:'{', secAcc:'#0891b2', secAccD:'#0e7490', secAccSoft:'#cffafe' }, { id:'p12', num:'§ 12', name:'Уравнение окружности', sub:'$(x-a)^2+(y-b)^2=r^2$', watermark:'○', secAcc:'#db2777', secAccD:'#9d174d', secAccSoft:'#fce7f3' }, { id:'p13', num:'§ 13', name:'Метод интервалов', sub:'неравенства', watermark:'>0', secAcc:'#059669', secAccD:'#047857', secAccSoft:'#d1fae5' }, ], achLabels: { start:'Начало главы 3!', p11_done:'Системы нелинейных уравнений освоены!', p12_done:'Уравнение окружности освоено!', p13_done:'Метод интервалов освоен!', ch3_done:'Глава 3 пройдена!', }, tips: [ { sec:'p10', html:'Дробно-рациональное уравнение $\\dfrac{P(x)}{Q(x)} = 0$ равносильно системе: $P(x) = 0$ и $Q(x) \\ne 0$.' }, { sec:'p11', html:'В системах нелинейных уравнений часто помогает метод подстановки или сложение.' }, { sec:'p12', html:'Длина отрезка: $d = \\sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}$. Окружность: $(x - a)^2 + (y - b)^2 = r^2$.' }, { sec:'p13', html:'Метод интервалов: нули → точки на оси → знаки на промежутках.' }, { sec:'final3', html:'4 босса главы 3.' }, ], sidebars: { p10:[ ['Дробно-рац. уравн.','$\\dfrac{P(x)}{Q(x)} = 0$'], ['Условие','$P(x) = 0$ и $Q(x) \\ne 0$'], ['Алгоритм','найди корни $P$ → проверь ОДЗ'] ], p11:[ ['Система','несколько уравнений с общими $x, y$'], ['Подстановка','выразил → подставил'], ['Графически','точки пересечения графиков'] ], p12:[ ['Длина','$d = \\sqrt{(x_2-x_1)^2 + (y_2-y_1)^2}$'], ['Окружность','$(x-a)^2 + (y-b)^2 = r^2$'], ['Центр','$(a; b)$'], ['Радиус','$r$'] ], p13:[ ['Шаг 1','перенеси всё влево, приведи к виду $\\dfrac{P}{Q}$'], ['Шаг 2','найди нули $P$ и $Q$'], ['Шаг 3','отметь на оси'], ['Шаг 4','определи знаки'] ], final3:[ ['§§10–13','теория главы 3'], ['Боссов','4'], ['Награда','+100 XP'] ], }, }, { chN: 4, title: 'Прогрессии', sub: 'Последовательности · арифметическая · геометрическая', heroH2: 'Прогрессии — арифметика и геометрия чисел', heroP: 'Здесь мы изучаем числовые последовательности, арифметическую прогрессию $(a_n = a_1 + (n-1)d)$ и геометрическую прогрессию $(b_n = b_1 q^{n-1})$, формулы сумм $n$ первых членов и сумму бесконечно убывающей геометрической прогрессии $S = \\dfrac{b_1}{1 - q}$.', palette: { pri:'#0891b2', pri2:'#0e7490', priSoft:'#cffafe', acc:'#22d3ee', acc2:'#0891b2', accSoft:'#ecfeff', hdrGrad:'linear-gradient(110deg,#164e63 0%,#0891b2 55%,#22d3ee 100%)', hdrShadow:'rgba(165,243,252,.2)', hdrWmStroke:'rgba(209,250,255,.12)', darkBg:'#04141a', darkCard:'#0a1b22', darkCardSoft:'#0d2229', darkText:'#e0fcff', darkMuted:'#7aa8b3', darkBorder:'#163842', confetti:['#0891b2','#22d3ee','#67e8f9','#f59e0b','#10b981'], heroWm:'aₙ', }, paras: [ { id:'p14', num:'§ 14', name:'Числовая последовательность', sub:'$a_1, a_2, \\dots, a_n$', watermark:'aₙ', secAcc:'#0891b2', secAccD:'#0e7490', secAccSoft:'#cffafe' }, { id:'p15', num:'§ 15', name:'Арифметическая прогрессия', sub:'$a_n = a_1 + (n-1)d$', watermark:'+d', secAcc:'#06b6d4', secAccD:'#0891b2', secAccSoft:'#cffafe' }, { id:'p16', num:'§ 16', name:'Сумма арифм. прогрессии', sub:'$S_n = \\tfrac{a_1 + a_n}{2} n$', watermark:'Σ', secAcc:'#2563eb', secAccD:'#1d4ed8', secAccSoft:'#dbeafe' }, { id:'p17', num:'§ 17', name:'Геометрическая прогрессия', sub:'$b_n = b_1 q^{n-1}$', watermark:'·q', secAcc:'#7c3aed', secAccD:'#6d28d9', secAccSoft:'#ede9fe' }, { id:'p18', num:'§ 18', name:'Сумма геом. прогрессии', sub:'$S_n = \\tfrac{b_1(q^n - 1)}{q - 1}$', watermark:'Σ', secAcc:'#db2777', secAccD:'#9d174d', secAccSoft:'#fce7f3' }, { id:'p19', num:'§ 19', name:'Бесконечно убывающая', sub:'$S = \\tfrac{b_1}{1 - q}$', watermark:'∞', secAcc:'#059669', secAccD:'#047857', secAccSoft:'#d1fae5' }, ], achLabels: { start:'Начало главы 4!', p15_done:'Арифметическая прогрессия освоена!', p17_done:'Геометрическая прогрессия освоена!', p19_done:'Бесконечно убывающая освоена!', ch4_done:'Глава 4 пройдена! Алгебра 9 — финал!', }, tips: [ { sec:'p14', html:'Числовая последовательность — это функция натурального аргумента: $a: \\mathbb{N} \\to \\mathbb{R}$.' }, { sec:'p15', html:'В арифметической прогрессии разность $d = a_{n+1} - a_n$ — постоянна.' }, { sec:'p16', html:'$S_n = \\dfrac{(a_1 + a_n) n}{2} = \\dfrac{(2 a_1 + (n - 1) d) n}{2}$.' }, { sec:'p17', html:'В геометрической прогрессии знаменатель $q = \\dfrac{b_{n+1}}{b_n}$ — постоянен.' }, { sec:'p18', html:'$S_n = \\dfrac{b_1 (q^n - 1)}{q - 1}$ при $q \\ne 1$.' }, { sec:'p19', html:'При $|q| < 1$: $S = \\dfrac{b_1}{1 - q}$.' }, { sec:'final4', html:'6 боссов главы 4. После — вся Алгебра 9 в твоём арсенале!' }, ], sidebars: { p14:[ ['Послед-сть','$(a_n)$, $n \\in \\mathbb{N}$'], ['Способы','формула $n$-го члена, реккурентно, словесно'], ['Член','$a_n$ — $n$-й член'] ], p15:[ ['Опр.','$a_{n+1} - a_n = d$'], ['Форм.','$a_n = a_1 + (n - 1) d$'], ['Свойство','$a_n = \\tfrac{a_{n-1} + a_{n+1}}{2}$'] ], p16:[ ['Формула 1','$S_n = \\tfrac{a_1 + a_n}{2} n$'], ['Формула 2','$S_n = \\tfrac{2 a_1 + (n - 1) d}{2} n$'] ], p17:[ ['Опр.','$\\dfrac{b_{n+1}}{b_n} = q$, $b_1 \\ne 0$, $q \\ne 0$'], ['Форм.','$b_n = b_1 q^{n-1}$'], ['Свойство','$b_n^2 = b_{n-1} b_{n+1}$'] ], p18:[ ['$q \\ne 1$','$S_n = \\tfrac{b_1(q^n - 1)}{q - 1}$'], ['$q = 1$','$S_n = n \\cdot b_1$'] ], p19:[ ['Условие','$|q| < 1$'], ['Сумма','$S = \\tfrac{b_1}{1 - q}$'] ], final4:[ ['§§14–19','теория главы 4'], ['Боссов','6'], ['Награда','+100 XP'], ['Алгебра 9','полностью пройдена!'] ], }, }, ]; /* ===== HTML generator ===== */ function genChapter(ch) { const chN = ch.chN; const P = ch.palette; const paras = ch.paras; const allParas = [...paras, { id:'final'+chN, num:'★', name:'Финал главы', sub:'Итоги · '+paras.length+' боссов', final:true, watermark:'★', secAcc:P.pri, secAccD:P.pri2, secAccSoft:P.priSoft }]; const total = allParas.length; const slug = `algebra-9-ch${chN}`; const lsPrefix = `algebra9_ch${chN}`; // Build section colors block const secColors = allParas.map(p => `.sec[id="sec-${p.id}"]{ --sec-acc:${p.secAcc}; --sec-acc-d:${p.secAccD}; --sec-acc-soft:${p.secAccSoft}; }` ).join('\n'); // Build section html const secsHtml = allParas.map(p => { if (p.final) { return `
Финал главы

Итоги. ${paras.length} боссов главы ${chN}

`; } return `
${p.num}

${p.name}

`; }).join('\n'); // Builders const builders = allParas.map(p => { const fnName = 'build' + p.id.charAt(0).toUpperCase() + p.id.slice(1); const titleText = p.final ? 'Финал главы — в разработке' : `«${p.name}»`; const numLabel = p.final ? '★' : p.num; const xpHint = p.final ? '

Боссы и итоговые задания будут добавлены в Phase 1.

' : '

Раздел Phase 1.

'; return `function ${fnName}(){ const root = document.getElementById('${p.id}-body'); root.innerHTML = \`
\${ICONS.theory} В разработке ${numLabel}

Содержание ${p.final ? 'финала главы' : 'параграфа'} ${titleText} будет добавлено в следующих обновлениях.

${xpHint}
\` + secNav(${p.idx > 0 ? `'${allParas[p.idx-1].id}'` : 'null'}, ${p.idx < allParas.length-1 ? `'${allParas[p.idx+1].id}'` : 'null'}) + readButton('${p.id}'); renderMath(root); wireReadBtn('${p.id}'); }`; }); // Add idx allParas.forEach((p, i) => p.idx = i); // Re-generate builders now that idx is set const buildersText = allParas.map(p => { const fnName = 'build' + p.id.charAt(0).toUpperCase() + p.id.slice(1); const titleText = p.final ? 'Финал главы — в разработке' : `«${p.name}»`; const numLabel = p.final ? '★' : p.num; const xpHint = p.final ? '

Боссы и итоговые задания будут добавлены в Phase 1.

' : '

Раздел Phase 1.

'; const prev = p.idx > 0 ? `'${allParas[p.idx-1].id}'` : 'null'; const next = p.idx < allParas.length-1 ? `'${allParas[p.idx+1].id}'` : 'null'; return `function ${fnName}(){ const root = document.getElementById('${p.id}-body'); root.innerHTML = \`
\${ICONS.theory} В разработке ${numLabel}

Содержание ${p.final ? 'финала главы' : 'параграфа'} ${titleText} будет добавлено в следующих обновлениях.

${xpHint}
\` + secNav(${prev}, ${next}) + readButton('${p.id}'); renderMath(root); wireReadBtn('${p.id}'); }`; }).join('\n\n'); // PARAS array literal const parasLit = allParas.map(p => { if (p.final) return ` { id:'${p.id}', num:'${p.num}', name:'${p.name}', sub:'${p.sub.replace(/'/g,"\\'")}', final:true }`; return ` { id:'${p.id}', num:'${p.num}', name:'${p.name}', sub:'${p.sub.replace(/'/g,"\\'")}' }`; }).join(',\n'); // BUILDERS map const buildersMap = allParas.map(p => `${p.id}:()=>build${p.id.charAt(0).toUpperCase()+p.id.slice(1)}()`).join(', '); // SIDEBARS literal const sidebarsLit = Object.entries(ch.sidebars).map(([id, rows]) => { const r = rows.map(([k, v]) => `['${k.replace(/'/g,"\\'")}','${v.replace(/'/g,"\\'")}']`).join(','); const title = id.startsWith('final') ? 'Финал главы' : 'Шпаргалка \\xA7'+id.replace('p',''); return ` ${id}:{title:'${title}',rows:[${r}]}`; }).join(',\n'); // TIPS literal const tipsLit = ch.tips.map(t => ` {sec:'${t.sec}',html:'${t.html.replace(/'/g,"\\'")}'}`).join(',\n'); // ACH_LABELS literal const achLit = Object.entries(ch.achLabels).map(([k, v]) => ` ${k}:'${v.replace(/'/g,"\\'")}'`).join(',\n'); // initial progress object const progressInit = allParas.map(p => `${p.id}:0`).join(','); // sec NAMES map const secNames = allParas.map(p => { const label = p.final ? "'Финал'" : `'\\xA7${p.id.replace('p','')}'`; return `${p.id}:${label}`; }).join(','); const firstId = allParas[0].id; return ` Алгебра 9 · Глава ${chN} · ${ch.title}

Алгебра 9 · Глава ${chN}

${ch.sub}
К алгебре 9

${ch.heroH2}

${ch.heroP}

Прогресс по главе
0%
Параграфы главы
${secsHtml}
Достижение!
`; } /* ===== Write all 4 files ===== */ for (const ch of CHAPTERS) { const fp = path.join(OUT_DIR, `algebra_9_ch${ch.chN}.html`); const html = genChapter(ch); fs.writeFileSync(fp, html, 'utf8'); console.log(`[gen] Wrote ${fp} (${html.length} bytes)`); } console.log('[gen] Done — Phase 0 chapters generated.');