0c0eea7a6b
- gen_phys9_hub.js: генератор hub из physics_10_hub.html (blue palette, 5 cards) - gen_phys9_ch.js: генератор 5 файлов глав со STUB-builder'ами по канве physics_10_ch - 038_physics_9_hub.sql: переразмечает physics-9 как hub + 5 дочерних (ch1-ch5) - Глава 5 — Лабораторный практикум, 12 ЛР с поддержкой lr-id вместо § Источник: Исаченкова, Сокольский, Захаревич "Физика 9" (Народная асвета, 2019). Контент в Phase 5 — авторский (наш материал).
241 lines
13 KiB
JavaScript
241 lines
13 KiB
JavaScript
// Генератор physics_9_hub.html на основе physics_10_hub.html
|
||
// Палитра: blue (вместо amber у Phys 10), 5 глав, заголовки/описания Физики 9.
|
||
'use strict';
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const SRC = path.join(__dirname, '..', '..', 'frontend', 'textbooks', 'physics_10_hub.html');
|
||
const DST = path.join(__dirname, '..', '..', 'frontend', 'textbooks', 'physics_9_hub.html');
|
||
|
||
let h = fs.readFileSync(SRC, 'utf8');
|
||
|
||
// === 1. Primary palette: amber (#ca8a04 / #fde047) → blue (#2563eb / #60a5fa) ===
|
||
h = h.replace(
|
||
/:root\{[\s\S]*?--sh-h:0 12px 36px rgba\(202,138,4,\.18\);[\s\S]*?\}/,
|
||
`:root{
|
||
--bg:#eff6ff; --card:#fff;
|
||
--text:#0f172a; --muted:#475569;
|
||
--border:#bfdbfe;
|
||
--pri:#2563eb; --pri-d:#1d4ed8;
|
||
--pri-soft:#dbeafe;
|
||
--ch1:#2563eb; --ch1-d:#1d4ed8;
|
||
--ch2:#059669; --ch2-d:#047857;
|
||
--ch3:#7c3aed; --ch3-d:#6d28d9;
|
||
--ch4:#db2777; --ch4-d:#be185d;
|
||
--ch5:#0891b2; --ch5-d:#0e7490;
|
||
--sh:0 4px 16px rgba(37,99,235,.10);
|
||
--sh-h:0 12px 36px rgba(37,99,235,.18);
|
||
}`);
|
||
|
||
h = h.replace(
|
||
/html\.dark\{[\s\S]*?--pri-soft:rgba\(202,138,4,\.16\);[\s\S]*?\}/,
|
||
`html.dark{
|
||
--bg:#0a1428; --card:#102137;
|
||
--text:#dbeafe; --muted:#93c5fd;
|
||
--border:#1e3a5f;
|
||
--pri-soft:rgba(37,99,235,.16);
|
||
}`);
|
||
|
||
// === 2. Header gradient: amber → blue ===
|
||
h = h.replace(
|
||
/\.hdr\{position:relative;background:linear-gradient\(110deg,#713f12 0%,#ca8a04 55%,#fde047 100%\)[^}]*\}/,
|
||
`.hdr{position:relative;background:linear-gradient(110deg,#1e3a8a 0%,#2563eb 55%,#60a5fa 100%);color:#fff;padding:32px 24px 28px;overflow:hidden;border-bottom:2px solid rgba(219,234,254,.18)}`);
|
||
|
||
h = h.replace(/rgba\(254,243,199,\.12\)/g, 'rgba(219,234,254,.12)');
|
||
h = h.replace(/rgba\(254,243,199,\.18\)/g, 'rgba(219,234,254,.18)');
|
||
|
||
// === 3. po-icon gradient + po-bar/po-fill ===
|
||
h = h.replace(
|
||
/\.po-icon\{[^}]*background:linear-gradient\(135deg,#ca8a04,#fde047\)[^}]*\}/,
|
||
`.po-icon{width:46px;height:46px;border-radius:12px;background:linear-gradient(135deg,#2563eb,#60a5fa);color:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:'Outfit',sans-serif;font-size:1.4rem;font-weight:900;font-style:italic}`);
|
||
h = h.replace(/\.po-bar\{height:8px;background:rgba\(202,138,4,\.14\)/, '.po-bar{height:8px;background:rgba(37,99,235,.14)');
|
||
h = h.replace(/\.po-fill\{height:100%;background:linear-gradient\(90deg,var\(--pri\),#fde047\)/, '.po-fill{height:100%;background:linear-gradient(90deg,var(--pri),#60a5fa)');
|
||
h = h.replace(/\.po-xp\{[^}]*background:linear-gradient\(135deg,#f59e0b,var\(--pri\)\)[^}]*\}/,
|
||
".po-xp{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;background:linear-gradient(135deg,#3b82f6,var(--pri));color:#fff;border-radius:99px;font-size:.8rem;font-weight:800;font-family:'Unbounded',sans-serif;letter-spacing:.02em;box-shadow:0 4px 12px rgba(37,99,235,.24)}");
|
||
|
||
// === 4. Final-head gradient ===
|
||
h = h.replace(
|
||
/\.final-head\{padding:18px 22px;background:linear-gradient\(135deg,#713f12 0%,#ca8a04 55%,#f59e0b 100%\)/,
|
||
'.final-head{padding:18px 22px;background:linear-gradient(135deg,#1e3a8a 0%,#2563eb 55%,#3b82f6 100%)');
|
||
|
||
// === 5. Title + H1 + subtitle ===
|
||
h = h.replace(/<title>Физика 10 класс — учебник<\/title>/, '<title>Физика 9 класс — учебник</title>');
|
||
h = h.replace(/<h1>Физика — 10 класс<\/h1>/, '<h1>Физика — 9 класс</h1>');
|
||
h = h.replace(
|
||
/<div class="hdr-sub">Полный курс физики 10 класса:[^<]+<\/div>/,
|
||
'<div class="hdr-sub">Полный курс механики: кинематика, динамика, статика, законы сохранения, 12 лабораторных работ</div>'
|
||
);
|
||
|
||
// === 6. localStorage keys + API endpoint ===
|
||
h = h.replace(/physics10_theme/g, 'physics9_theme');
|
||
h = h.replace(/physics10_xp/g, 'physics9_xp');
|
||
h = h.replace(/physics10_course_master/g, 'physics9_course_master');
|
||
h = h.replace(/physics10_course_bosses/g, 'physics9_course_bosses');
|
||
h = h.replace(/physics10-master/g, 'physics9-master');
|
||
h = h.replace(/'\/api\/textbooks\/physics-10\/children'/, "'/api/textbooks/physics-9/children'");
|
||
|
||
// === 7. Заменяем блок с 6 главами целиком на блок с 5 главами ===
|
||
const chBlock = `
|
||
<a href="/textbook/physics-9-ch1" class="ch-card ch1-card" id="ch-1">
|
||
<div class="ch-cover ch1">
|
||
<div class="ch-cover-wm">v</div>
|
||
<div class="ch-num">Глава 1</div>
|
||
<div class="ch-title">Основы кинематики</div>
|
||
<div class="ch-range">§1–§14</div>
|
||
</div>
|
||
<div class="ch-body">
|
||
<div class="ch-desc">Механическое движение, относительность, векторы, путь и перемещение, равномерное и равноускоренное движение, движение по окружности.</div>
|
||
<div class="ch-prog">
|
||
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-1">0%</span></div>
|
||
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-1" style="width:0%"></div></div>
|
||
</div>
|
||
<div class="ch-action">
|
||
<span id="btn-1">Открыть главу</span>
|
||
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</div>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/textbook/physics-9-ch2" class="ch-card ch2-card" id="ch-2">
|
||
<div class="ch-cover ch2">
|
||
<div class="ch-cover-wm">F</div>
|
||
<div class="ch-num">Глава 2</div>
|
||
<div class="ch-title">Основы динамики</div>
|
||
<div class="ch-range">§15–§24</div>
|
||
</div>
|
||
<div class="ch-body">
|
||
<div class="ch-desc">Законы Ньютона, масса, закон Гука, силы трения, движение под силой тяжести, всемирное тяготение, вес и невесомость.</div>
|
||
<div class="ch-prog">
|
||
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-2">0%</span></div>
|
||
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-2" style="width:0%"></div></div>
|
||
</div>
|
||
<div class="ch-action">
|
||
<span id="btn-2">Открыть главу</span>
|
||
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</div>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/textbook/physics-9-ch3" class="ch-card ch3-card" id="ch-3">
|
||
<div class="ch-cover ch3">
|
||
<div class="ch-cover-wm">M</div>
|
||
<div class="ch-num">Глава 3</div>
|
||
<div class="ch-title">Основы статики</div>
|
||
<div class="ch-range">§25–§30</div>
|
||
</div>
|
||
<div class="ch-body">
|
||
<div class="ch-desc">Условия равновесия, момент силы, рычаги, блоки, наклонная плоскость, КПД, центр тяжести, закон Архимеда.</div>
|
||
<div class="ch-prog">
|
||
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-3">0%</span></div>
|
||
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-3" style="width:0%"></div></div>
|
||
</div>
|
||
<div class="ch-action">
|
||
<span id="btn-3">Открыть главу</span>
|
||
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</div>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/textbook/physics-9-ch4" class="ch-card ch4-card" id="ch-4">
|
||
<div class="ch-cover ch4">
|
||
<div class="ch-cover-wm">p·E</div>
|
||
<div class="ch-num">Глава 4</div>
|
||
<div class="ch-title">Законы сохранения</div>
|
||
<div class="ch-range">§31–§36</div>
|
||
</div>
|
||
<div class="ch-body">
|
||
<div class="ch-desc">Импульс тела, закон сохранения импульса, реактивное движение, работа, мощность, кинетическая и потенциальная энергия, закон сохранения энергии.</div>
|
||
<div class="ch-prog">
|
||
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-4">0%</span></div>
|
||
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-4" style="width:0%"></div></div>
|
||
</div>
|
||
<div class="ch-action">
|
||
<span id="btn-4">Открыть главу</span>
|
||
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</div>
|
||
</div>
|
||
</a>
|
||
|
||
<a href="/textbook/physics-9-ch5" class="ch-card ch5-card" id="ch-5">
|
||
<div class="ch-cover ch5">
|
||
<div class="ch-cover-wm">Δt</div>
|
||
<div class="ch-num">Глава 5</div>
|
||
<div class="ch-title">Лабораторный практикум</div>
|
||
<div class="ch-range">ЛР 1–12</div>
|
||
</div>
|
||
<div class="ch-body">
|
||
<div class="ch-desc">12 лабораторных работ: погрешности, ускорение, окружность, закон Гука, трение, брошенное тело, рычаг, блоки, наклонная плоскость, Архимед, импульс, энергия.</div>
|
||
<div class="ch-prog">
|
||
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-5">0%</span></div>
|
||
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-5" style="width:0%"></div></div>
|
||
</div>
|
||
<div class="ch-action">
|
||
<span id="btn-5">Открыть главу</span>
|
||
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</div>
|
||
</div>
|
||
</a>
|
||
|
||
`;
|
||
|
||
// Replace the entire <a href="/textbook/physics-10-ch1"...</a> ... <a ch6></a> block (6 cards → 5 cards)
|
||
h = h.replace(/\s*<a href="\/textbook\/physics-10-ch1"[\s\S]*?<a href="\/textbook\/physics-10-ch6"[\s\S]*?<\/a>\s*/,
|
||
chBlock);
|
||
|
||
// === 8. final cta + master text ===
|
||
h = h.replace(/<div class="final-cta-title">Курс Физика 10 пройден!<\/div>/, '<div class="final-cta-title">Курс Физика 9 пройден!</div>');
|
||
h = h.replace(/«Магистр физики 10»/g, '«Магистр физики 9»');
|
||
h = h.replace(/Магистр физики 10/g, 'Магистр физики 9');
|
||
|
||
// final-head-sub
|
||
h = h.replace(
|
||
/<div class="final-head-sub">Шпаргалка курса и интегрированные боссы по всем 6 главам\. В разработке \(Phase 7\)\.<\/div>/,
|
||
'<div class="final-head-sub">Шпаргалка курса и интегрированные боссы по всем 5 главам. В разработке (Phase 7).</div>'
|
||
);
|
||
|
||
// fin-placeholder: 37 → 36, 6 → 5
|
||
h = h.replace(
|
||
/Итоговая шпаргалка по всем 37 параграфам и 8–10 интегрированных боссов появятся в Phase 7 \(после завершения всех 6 глав\)\./,
|
||
'Итоговая шпаргалка по всем 36 параграфам и 8–10 интегрированных боссов появятся в Phase 7 (после завершения всех 5 глав).'
|
||
);
|
||
|
||
// Footer
|
||
h = h.replace(/Интерактивный учебник «Физика — 10 класс»/, 'Интерактивный учебник «Физика — 9 класс»');
|
||
|
||
// Achievement strip
|
||
h = h.replace(/Прочитайте все 37 параграфов курса, чтобы получить достижение/, 'Прочитайте все 36 параграфов курса, чтобы получить достижение');
|
||
h = h.replace(/Вы прочитали весь курс физики 10 класса\./, 'Вы прочитали весь курс физики 9 класса.');
|
||
|
||
// === 9. TOTAL + CH_PARA + CH_IDX ===
|
||
h = h.replace(/var TOTAL = 37;[\s\S]*?var CH_IDX = \{[\s\S]*?\};/, `var TOTAL = 36;
|
||
var CH_PARA = {
|
||
'physics-9-ch1': 14,
|
||
'physics-9-ch2': 10,
|
||
'physics-9-ch3': 6,
|
||
'physics-9-ch4': 6,
|
||
'physics-9-ch5': 12,
|
||
};
|
||
var CH_IDX = {
|
||
'physics-9-ch1': 1,
|
||
'physics-9-ch2': 2,
|
||
'physics-9-ch3': 3,
|
||
'physics-9-ch4': 4,
|
||
'physics-9-ch5': 5,
|
||
};`);
|
||
|
||
// === 10. Chapter grid: 6 cards → 5 cards (2-1-2 на широких экранах смотрится лучше при 5) ===
|
||
// Оставим CSS как есть — repeat(3,1fr) на >=1000px и repeat(2,1fr) на >=680px.
|
||
// 5 карточек выстроятся как 3+2 (или 2+2+1). Это нормально.
|
||
|
||
fs.writeFileSync(DST, h);
|
||
console.log('OK hub →', DST, 'bytes:', h.length);
|
||
|
||
// Sanity: parse inline scripts
|
||
const scriptMatches = [...h.matchAll(/<script>([\s\S]*?)<\/script>/g)];
|
||
console.log('inline <script> count:', scriptMatches.length);
|
||
for (const m of scriptMatches) {
|
||
try { new Function(m[1]); }
|
||
catch(e) { console.error('JS PARSE FAIL:', e.message); process.exit(1); }
|
||
}
|
||
console.log('all inline JS parses OK');
|