c0af5502bf
Политика «все учебники наши»: нигде не упоминаются сторонние авторы. - Миграции (15 файлов): колонка author → 'LearnSpace'; из описаний убран оборот «по учебнику <автор>:»; авторские фамилии вычищены из комментариев. Покрыты Арефьева/Пирютко, Казаков, Латотин/Чеботаревский/Горбунова/Цыбулько, Исаченкова, Жилко/Маркович/Сокольский, Герасимов/Лобанов. - HTML: physics_9_ch5 («по канве учебника Исаченковой» → «по учебной программе»), physics_11_hub (hdr-sub с авторами → описание курса), mocks-redesign (карточки-авторы → LearnSpace). - Генераторы gen_phys9_ch.js/gen_phys11_stubs.js — шаблоны без авторов. - НОВОЕ: update_textbook_authors.js — идемпотентный апдейтер ЖИВОЙ БД (миграции уже применены): author→'LearnSpace' у всех 107 учебников + чистка описаний. DRY-RUN по умолч. ⚠️ Живую БД правит ПОЛЬЗОВАТЕЛЬ: node backend/scripts/update_textbook_authors.js --apply (в БД сейчас author пуст у всех, видимые упоминания были в описаниях «по учебнику …»). review_geom10/11.js не тронуты — там фамилии как поисковые шаблоны детектора, не атрибуция. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
464 lines
29 KiB
JavaScript
464 lines
29 KiB
JavaScript
#!/usr/bin/env node
|
||
'use strict';
|
||
/* Генератор stub-файлов для Физики 11 (W0).
|
||
* Запуск: node backend/scripts/gen_phys11_stubs.js
|
||
*/
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const OUT = path.join(__dirname, '..', '..', 'frontend', 'textbooks');
|
||
|
||
const CHAPTERS = [
|
||
{ n:1, slug:'physics-11-ch1', name:'Механические колебания и волны',
|
||
paraRange:'§1–§6', wm:'∿', themeName:'cyan',
|
||
gradient:['#155e75','#0891b2','#a5f3fc'],
|
||
pri:'#0891b2', pri2:'#0e7490', priSoft:'#cffafe',
|
||
desc:'Колебательное движение, гармонические колебания, маятники, превращения энергии, резонанс, продольные и поперечные волны, звук.',
|
||
paras:[
|
||
{n:1, title:'Колебательное движение. Гармонические колебания', sub:'$T = \\Delta t / N$, $\\nu = 1/T$, $\\omega = 2\\pi/T$, $x = A\\cos(\\omega t + \\varphi_0)$'},
|
||
{n:2, title:'Пружинный и математический маятники', sub:'$T_{пр} = 2\\pi\\sqrt{m/k}$, $T_{мат} = 2\\pi\\sqrt{l/g}$'},
|
||
{n:3, title:'Превращения энергии при гарм. колебаниях', sub:'$W_{мех} = kA^2/2 = m\\omega^2 A^2/2$'},
|
||
{n:4, title:'Свободные и вынужденные колебания. Резонанс', sub:'Затухание, диссипация, $\\omega_{рез} \\approx \\omega_0$'},
|
||
{n:5, title:'Распространение колебаний в упругой среде. Продольные и поперечные волны', sub:'$\\lambda = vT$'},
|
||
{n:6, title:'Звуковые волны', sub:'16 Гц – 20 кГц, $v_{зв}^{возд} \\approx 340$ м/с'}
|
||
]
|
||
},
|
||
{ n:2, slug:'physics-11-ch2', name:'Электромагнитные колебания и волны',
|
||
paraRange:'§7–§13', wm:'⚡', themeName:'violet',
|
||
gradient:['#5b21b6','#7c3aed','#c4b5fd'],
|
||
pri:'#7c3aed', pri2:'#5b21b6', priSoft:'#ede9fe',
|
||
desc:'Колебательный контур, формула Томсона, переменный ток, трансформатор, передача электроэнергии, ЭМ волны.',
|
||
paras:[
|
||
{n:7, title:'Колебательный контур. Свободные ЭМ колебания. Формула Томсона', sub:'$T = 2\\pi\\sqrt{LC}$'},
|
||
{n:8, title:'Вынужденные ЭМ колебания. Переменный ток', sub:'$i = I_0\\sin(\\omega t)$, $I = I_0/\\sqrt{2}$'},
|
||
{n:9, title:'Преобразование переменного тока. Трансформатор', sub:'$k = N_1/N_2 = U_1/U_2$'},
|
||
{n:10, title:'Производство, передача и потребление электроэнергии', sub:'ГЭС, ТЭС, АЭС; потери $P = I^2 R$'},
|
||
{n:11, title:'Экологические проблемы производства и передачи электроэнергии', sub:'ВЭС, СЭС, гео- и приливные'},
|
||
{n:12, title:'ЭМ волны. Шкала ЭМ волн', sub:'$c = 3 \\cdot 10^8$ м/с'},
|
||
{n:13, title:'Действие ЭМ излучения на живые организмы', sub:'Ионизирующее vs неионизирующее'}
|
||
]
|
||
},
|
||
{ n:3, slug:'physics-11-ch3', name:'Оптика',
|
||
paraRange:'§14–§23', wm:'◇', themeName:'amber',
|
||
gradient:['#b45309','#d97706','#fcd34d'],
|
||
pri:'#d97706', pri2:'#b45309', priSoft:'#fef3c7',
|
||
desc:'Электромагнитная природа света, интерференция, дифракция, отражение, зеркала, преломление, тонкая линза, оптические приборы.',
|
||
paras:[
|
||
{n:14, title:'ЭМ природа света. Скорость света', sub:'Опыты Рёмера, Майкельсона'},
|
||
{n:15, title:'Интерференция света', sub:'$\\Delta = k\\lambda$ (max), $\\Delta = (2k+1)\\lambda/2$ (min)'},
|
||
{n:16, title:'Принцип Гюйгенса – Френеля. Дифракция. Дифракционная решётка', sub:'$d\\sin\\varphi = k\\lambda$'},
|
||
{n:17, title:'Прямолинейное распространение и отражение света. Зеркала', sub:'$\\angle_{пад} = \\angle_{отр}$'},
|
||
{n:18, title:'Сферические зеркала. Построение изображений', sub:'$\\frac{1}{F} = \\frac{1}{d} + \\frac{1}{f}$'},
|
||
{n:19, title:'Закон преломления света. Полное отражение', sub:'$n_1\\sin\\alpha = n_2\\sin\\beta$, $\\sin\\alpha_{пр} = 1/n$'},
|
||
{n:20, title:'Прохождение света через оптические элементы', sub:'Призмы, оптоволокно'},
|
||
{n:21, title:'Формула тонкой линзы', sub:'$D = 1/F$, $\\Gamma = f/d$'},
|
||
{n:22, title:'Оптические приборы для действительных изображений', sub:'Фотоаппарат, проектор'},
|
||
{n:23, title:'Оптические приборы для увеличения угла зрения', sub:'Лупа, микроскоп, телескоп'}
|
||
]
|
||
},
|
||
{ n:4, slug:'physics-11-ch4', name:'Основы СТО',
|
||
paraRange:'§24–§26', wm:'c', themeName:'blue',
|
||
gradient:['#1e3a8a','#2563eb','#93c5fd'],
|
||
pri:'#2563eb', pri2:'#1d4ed8', priSoft:'#dbeafe',
|
||
desc:'Принцип относительности Галилея, постулаты Эйнштейна, преобразования Лоренца, релятивистская динамика, E=mc².',
|
||
paras:[
|
||
{n:24, title:'Принцип относ. Галилея и ЭМ явления. Эксп. предпосылки СТО', sub:'Опыт Майкельсона – Морли'},
|
||
{n:25, title:'Постулаты специальной теории относительности', sub:'$\\Delta t = \\gamma\\Delta t_0$, $l = l_0/\\gamma$'},
|
||
{n:26, title:'Элементы релятивистской динамики. Взаимосвязь массы и энергии', sub:'$E_0 = mc^2$, $E^2 = (mc^2)^2 + (pc)^2$'}
|
||
]
|
||
},
|
||
{ n:5, slug:'physics-11-ch5', name:'Фотоны. Действия света',
|
||
paraRange:'§27–§29', wm:'γ', themeName:'pink',
|
||
gradient:['#831843','#db2777','#fbcfe8'],
|
||
pri:'#db2777', pri2:'#9d174d', priSoft:'#fce7f3',
|
||
desc:'Фотоэффект, квантовая гипотеза Планка, фотон, уравнение Эйнштейна, давление света, корпускулярно-волновой дуализм.',
|
||
paras:[
|
||
{n:27, title:'Фотоэффект. Эксперим. законы. Квантовая гипотеза Планка', sub:'$E = h\\nu$, $h = 6{,}63 \\cdot 10^{-34}$ Дж·с'},
|
||
{n:28, title:'Фотон. Уравнение Эйнштейна для фотоэффекта', sub:'$h\\nu = A_{вых} + \\frac{mv_{max}^2}{2}$'},
|
||
{n:29, title:'Давление света. Корпускулярно-волновой дуализм', sub:'$p_{фот} = h\\nu/c$. Опыт Лебедева'}
|
||
]
|
||
},
|
||
{ n:6, slug:'physics-11-ch6', name:'Физика атома',
|
||
paraRange:'§30–§34', wm:'⚛', themeName:'emerald',
|
||
gradient:['#065f46','#10b981','#a7f3d0'],
|
||
pri:'#10b981', pri2:'#047857', priSoft:'#d1fae5',
|
||
desc:'Ядерная модель атома Резерфорда, квантовые постулаты Бора, спектры испускания и поглощения, лазеры.',
|
||
paras:[
|
||
{n:30, title:'Сложное строение атома. Ядерная модель атома', sub:'Опыт Резерфорда, размер ядра $\\sim 10^{-15}$ м'},
|
||
{n:31, title:'Квантовые постулаты Бора', sub:'$E_n = -E_1/n^2 = -13{,}6/n^2$ эВ'},
|
||
{n:32, title:'Излучение и поглощение света атомом. Спектры', sub:'$h\\nu = E_n - E_m$, линейчатые спектры'},
|
||
{n:33, title:'Спонтанное и индуцированное излучение', sub:'Подготовка к лазерам'},
|
||
{n:34, title:'Лазеры', sub:'Инверсная населённость, когерентность'}
|
||
]
|
||
},
|
||
{ n:7, slug:'physics-11-ch7', name:'Ядерная физика и элементарные частицы',
|
||
paraRange:'§35–§44', wm:'☢', themeName:'rose',
|
||
gradient:['#7f1d1d','#dc2626','#fca5a5'],
|
||
pri:'#dc2626', pri2:'#991b1b', priSoft:'#fee2e2',
|
||
desc:'Протонно-нейтронная модель ядра, ядерные реакции, энергия связи, радиоактивность, ядерный реактор, термояд, элементарные частицы.',
|
||
paras:[
|
||
{n:35, title:'Протонно-нейтронная модель строения ядра атома', sub:'$A = Z + N$, изотопы'},
|
||
{n:36, title:'Ядерные реакции. Законы сохранения в ядерных реакциях', sub:'Сохранение заряда, нуклонов, энергии'},
|
||
{n:37, title:'Энергия связи ядра атома', sub:'$E_{св} = \\Delta m \\cdot c^2$, $\\Delta m = Zm_p + Nm_n - m_я$'},
|
||
{n:38, title:'Радиоактивность', sub:'$\\alpha$, $\\beta$, $\\gamma$ распады'},
|
||
{n:39, title:'Закон радиоактивного распада', sub:'$N = N_0 \\cdot 2^{-t/T}$, период полураспада $T$'},
|
||
{n:40, title:'Деление тяжёлых ядер. Цепные ядерные реакции', sub:'$^{235}$U, $k$ — коэф. размножения'},
|
||
{n:41, title:'Ядерный реактор', sub:'Управляющие стержни, замедлитель'},
|
||
{n:42, title:'Реакции ядерного синтеза', sub:'Термояд, $^2$H + $^3$H $\\to ^4$He + n'},
|
||
{n:43, title:'Ионизирующее излучение. Элементы дозиметрии', sub:'Доза $D$, эквивалент $H$, зиверт'},
|
||
{n:44, title:'Элементарные частицы и их взаимодействия', sub:'Стандартная модель, 4 фундаментальных взаимодействия'}
|
||
]
|
||
},
|
||
{ n:8, slug:'physics-11-ch8', name:'Основы единой физической картины мира',
|
||
paraRange:'§45', wm:'∞', themeName:'indigo',
|
||
gradient:['#3730a3','#6366f1','#c7d2fe'],
|
||
pri:'#6366f1', pri2:'#4338ca', priSoft:'#e0e7ff',
|
||
desc:'Современная естественнонаучная картина мира, эволюция физических теорий, четыре фундаментальных взаимодействия.',
|
||
paras:[
|
||
{n:45, title:'Современная естественнонаучная картина мира', sub:'Эволюция представлений: механика → ЭМ → квант'}
|
||
]
|
||
}
|
||
];
|
||
|
||
function makeChapter(c){
|
||
/* В какой волне будет реализована эта глава (см. PLAN_FIZIKA_11.md) */
|
||
const waveOf = {1:'W1-W2', 2:'W3-W4', 3:'W5-W7', 4:'W8', 5:'W9', 6:'W10-W11', 7:'W12-W13', 8:'W14'};
|
||
const wave = waveOf[c.n] || 'W1+';
|
||
const parasHtml = c.paras.map(p => `
|
||
<article class="para-card">
|
||
<div class="para-num">§ ${p.n}</div>
|
||
<div class="para-body">
|
||
<h2 class="para-title">${p.title}</h2>
|
||
<p class="para-sub">${p.sub}</p>
|
||
<div class="para-status">
|
||
<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||
Будет добавлено в волне ${wave}
|
||
</div>
|
||
</div>
|
||
</article>`).join('\n');
|
||
|
||
return `<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||
<title>Физика 11 · Глава ${c.n} · ${c.name}</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&family=Inter:wght@400;500;600;700&family=Unbounded:wght@400;700;800;900&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
|
||
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\\\[',right:'\\\\]',display:true},{left:'\\\\(',right:'\\\\)',display:false}],throwOnError:false})"></script>
|
||
<script src="/js/api.js" defer></script>
|
||
<script src="/js/xp.js" defer></script>
|
||
<script src="/js/phys-fx.js?v=1" defer></script>
|
||
<style>
|
||
:root{
|
||
--bg:#f8fafc; --card:#fff;
|
||
--text:#0f172a; --muted:#475569;
|
||
--border:#e2e8f0;
|
||
--pri:${c.pri}; --pri-d:${c.pri2};
|
||
--pri-soft:${c.priSoft};
|
||
--dark:${c.gradient[0]};
|
||
--sh:0 4px 16px rgba(0,0,0,.06);
|
||
}
|
||
html.dark{
|
||
--bg:#020617; --card:#0a1929;
|
||
--text:#dbeafe; --muted:#94a3b8;
|
||
--border:#1e293b;
|
||
}
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
html,body{min-height:100vh}
|
||
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;transition:background .25s,color .25s}
|
||
|
||
.hdr{position:relative;background:linear-gradient(110deg,${c.gradient[0]} 0%,${c.gradient[1]} 55%,${c.gradient[2]} 100%);color:#fff;padding:32px 24px 28px;overflow:hidden}
|
||
.hdr::before{content:'${c.wm}';position:absolute;right:8px;top:-20%;font-family:'Outfit',sans-serif;font-size:clamp(8rem,22vw,18rem);font-weight:900;color:rgba(255,255,255,.10);line-height:1;pointer-events:none;user-select:none}
|
||
.hdr-inner{position:relative;z-index:1;max-width:1100px;margin:0 auto;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
|
||
.hdr-back{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:rgba(255,255,255,.14);border-radius:9px;color:#fff;text-decoration:none;font-size:.85rem;font-weight:600;transition:background .15s}
|
||
.hdr-back:hover{background:rgba(255,255,255,.24)}
|
||
.hdr h1{font-family:'Outfit',sans-serif;font-size:1.7rem;font-weight:900;letter-spacing:-.01em}
|
||
.hdr-sub{font-size:.92rem;opacity:.85;margin-top:4px}
|
||
.ic{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
|
||
|
||
main{max-width:980px;margin:0 auto;padding:32px 24px 60px}
|
||
|
||
.intro-card{background:var(--card);border:1.5px solid var(--border);border-radius:16px;padding:22px 26px;margin-bottom:28px;box-shadow:var(--sh)}
|
||
.intro-num{display:inline-block;padding:4px 10px;background:var(--pri-soft);color:var(--pri-d);border-radius:99px;font-size:.72rem;font-weight:800;letter-spacing:.06em;margin-bottom:8px;text-transform:uppercase}
|
||
.intro-card h2{font-family:'Outfit',sans-serif;font-size:1.4rem;font-weight:800;margin-bottom:6px}
|
||
.intro-card p{color:var(--muted);font-size:.95rem}
|
||
|
||
.para-grid{display:grid;grid-template-columns:1fr;gap:14px}
|
||
.para-card{background:var(--card);border:1.5px solid var(--border);border-radius:14px;padding:18px 20px;display:flex;gap:16px;align-items:flex-start;transition:transform .15s,box-shadow .15s,border-color .15s}
|
||
.para-card:hover{transform:translateY(-2px);box-shadow:var(--sh);border-color:var(--pri)}
|
||
.para-num{font-family:'Outfit',sans-serif;font-size:1rem;font-weight:900;color:#fff;background:linear-gradient(135deg,var(--pri),var(--pri-d));padding:8px 12px;border-radius:9px;min-width:56px;text-align:center;letter-spacing:-.02em;flex-shrink:0}
|
||
.para-body{flex:1}
|
||
.para-title{font-family:'Outfit',sans-serif;font-size:1.05rem;font-weight:800;margin-bottom:4px;color:var(--text)}
|
||
.para-sub{font-size:.88rem;color:var(--muted);margin-bottom:10px;line-height:1.55}
|
||
.para-status{display:inline-flex;align-items:center;gap:6px;font-size:.78rem;color:var(--muted);background:rgba(0,0,0,.04);padding:6px 10px;border-radius:8px;font-weight:600}
|
||
html.dark .para-status{background:rgba(255,255,255,.06)}
|
||
.para-status .ic{width:14px;height:14px}
|
||
|
||
.banner-soon{margin-top:30px;text-align:center;padding:20px;background:linear-gradient(135deg,var(--pri-soft),transparent);border:1px dashed var(--pri);border-radius:14px;color:var(--pri-d);font-weight:700;font-size:.92rem}
|
||
.banner-soon b{font-family:'Outfit',sans-serif}
|
||
|
||
.foot{text-align:center;padding:24px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="hdr">
|
||
<div class="hdr-inner">
|
||
<div>
|
||
<a href="/textbook/physics-11" class="hdr-back">
|
||
<svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
|
||
К курсу физики 11
|
||
</a>
|
||
</div>
|
||
<div>
|
||
<h1>Глава ${c.n}. ${c.name}</h1>
|
||
<div class="hdr-sub">${c.desc.split('.')[0]} · ${c.paraRange}</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
|
||
<div class="intro-card">
|
||
<span class="intro-num">Глава ${c.n}</span>
|
||
<h2>${c.name}</h2>
|
||
<p>${c.desc} Глава содержит ${c.paras.length} параграф${c.paras.length === 1 ? '' : (c.paras.length < 5 ? 'а' : 'ов')} и финальный этап с боссами.</p>
|
||
</div>
|
||
|
||
<div class="para-grid">
|
||
${parasHtml}
|
||
</div>
|
||
|
||
<div class="banner-soon">
|
||
<b>Глава в разработке.</b> Полная реализация — в следующих волнах. Базовая библиотека <code>phys-fx.js</code> уже доступна.
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<footer class="foot">
|
||
Физика — 11 класс · Глава ${c.n} · LearnSpace
|
||
</footer>
|
||
|
||
</body>
|
||
</html>
|
||
`;
|
||
}
|
||
|
||
function makeHub(){
|
||
const cards = CHAPTERS.map((c, i) => `
|
||
<a href="/textbook/${c.slug}" class="ch-card" style="--ch:${c.pri};--ch-d:${c.pri2};--ch-soft:${c.priSoft}">
|
||
<div class="ch-cover" style="background:linear-gradient(135deg,${c.gradient[0]},${c.gradient[1]} 60%,${c.gradient[2]})">
|
||
<div class="ch-cover-wm">${c.wm}</div>
|
||
<div class="ch-num">Глава ${c.n}</div>
|
||
<div class="ch-title">${c.name}</div>
|
||
<div class="ch-range">${c.paraRange} + Финал</div>
|
||
</div>
|
||
<div class="ch-body">
|
||
<div class="ch-desc">${c.desc}</div>
|
||
<div class="ch-prog">
|
||
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-${c.n}">0%</span></div>
|
||
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-${c.n}" style="width:0%"></div></div>
|
||
</div>
|
||
<div class="ch-action">
|
||
<span id="btn-${c.n}">Открыть главу</span>
|
||
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</div>
|
||
</div>
|
||
</a>`).join('\n');
|
||
|
||
return `<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||
<title>Физика 11 класс — учебник</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&family=Inter:wght@400;500;600;700&family=Unbounded:wght@400;700;800;900&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
|
||
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\\\[',right:'\\\\]',display:true},{left:'\\\\(',right:'\\\\)',display:false}],throwOnError:false})"></script>
|
||
<script src="/js/api.js" defer></script>
|
||
<script src="/js/xp.js" defer></script>
|
||
<style>
|
||
:root{
|
||
--bg:#ecfeff; --card:#fff;
|
||
--text:#0f172a; --muted:#475569;
|
||
--border:#a5f3fc;
|
||
--pri:#0891b2; --pri-d:#0e7490;
|
||
--pri-soft:#cffafe;
|
||
--sh:0 4px 16px rgba(8,145,178,.10);
|
||
--sh-h:0 12px 36px rgba(8,145,178,.18);
|
||
}
|
||
html.dark{
|
||
--bg:#062326; --card:#0a2e35;
|
||
--text:#cffafe; --muted:#67e8f9;
|
||
--border:#0f4750;
|
||
}
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
html,body{min-height:100vh}
|
||
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;transition:background .25s,color .25s}
|
||
|
||
.hdr{position:relative;background:linear-gradient(110deg,#155e75 0%,#0891b2 55%,#67e8f9 100%);color:#fff;padding:32px 24px 28px;overflow:hidden;border-bottom:2px solid rgba(165,243,252,.18)}
|
||
.hdr::before{content:'ФИЗИКА';position:absolute;right:-14px;top:-18%;font-family:'Outfit',sans-serif;font-size:clamp(5rem,16vw,13rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(207,250,254,.12);line-height:1;pointer-events:none;user-select:none}
|
||
.hdr-inner{position:relative;z-index:1;max-width:1180px;margin:0 auto;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
|
||
.hdr-back{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:rgba(255,255,255,.14);border-radius:9px;color:#fff;text-decoration:none;font-size:.85rem;font-weight:600;transition:background .15s}
|
||
.hdr-back:hover{background:rgba(255,255,255,.24)}
|
||
.hdr h1{font-family:'Outfit',sans-serif;font-size:1.85rem;font-weight:900;letter-spacing:-.01em}
|
||
.hdr-sub{font-size:.92rem;opacity:.88;margin-top:4px}
|
||
.ic{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
|
||
|
||
main{max-width:1180px;margin:0 auto;padding:32px 24px 60px}
|
||
|
||
.prog-overall{background:linear-gradient(135deg,var(--pri-soft),rgba(103,232,249,.12));border:1px solid var(--border);border-radius:14px;padding:14px 18px;margin-bottom:28px;display:flex;gap:14px;align-items:center;flex-wrap:wrap}
|
||
.po-icon{width:46px;height:46px;border-radius:12px;background:linear-gradient(135deg,#0891b2,#67e8f9);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}
|
||
.po-text{flex:1;min-width:160px}
|
||
.po-label{font-size:.78rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:4px}
|
||
.po-bar{height:8px;background:rgba(8,145,178,.14);border-radius:5px;overflow:hidden;margin-top:6px}
|
||
.po-fill{height:100%;background:linear-gradient(90deg,var(--pri),#67e8f9);border-radius:5px;transition:width .5s}
|
||
|
||
.ch-grid{display:grid;grid-template-columns:1fr;gap:18px;margin-bottom:30px}
|
||
@media(min-width:680px){.ch-grid{grid-template-columns:1fr 1fr}}
|
||
@media(min-width:1100px){.ch-grid{grid-template-columns:repeat(4,1fr)}}
|
||
|
||
.ch-card{background:var(--card);border:1.5px solid var(--border);border-radius:18px;overflow:hidden;display:flex;flex-direction:column;transition:transform .2s,box-shadow .2s,border-color .2s;cursor:pointer;text-decoration:none;color:inherit}
|
||
.ch-card:hover{transform:translateY(-4px);box-shadow:var(--sh-h)}
|
||
.ch-cover{padding:22px 22px 18px;color:#fff;position:relative;overflow:hidden}
|
||
.ch-cover-wm{position:absolute;right:-8px;top:-22px;font-size:5.2rem;font-weight:900;font-family:'Outfit',sans-serif;line-height:1;color:rgba(255,255,255,.20);pointer-events:none;letter-spacing:-.04em}
|
||
.ch-num{display:inline-block;padding:4px 10px;background:rgba(255,255,255,.22);border-radius:99px;font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;position:relative;z-index:1}
|
||
.ch-title{font-family:'Outfit',sans-serif;font-size:1.05rem;font-weight:800;letter-spacing:-.01em;position:relative;z-index:1;line-height:1.3}
|
||
.ch-range{font-size:.82rem;opacity:.88;margin-top:4px;position:relative;z-index:1;font-weight:500}
|
||
|
||
.ch-body{padding:16px 20px 18px;display:flex;flex-direction:column;flex:1}
|
||
.ch-desc{font-size:.86rem;color:var(--text);opacity:.84;flex:1;margin-bottom:12px;line-height:1.55}
|
||
|
||
.ch-prog{margin-bottom:12px}
|
||
.ch-prog-label{display:flex;justify-content:space-between;font-size:.74rem;color:var(--muted);font-weight:600;margin-bottom:4px}
|
||
.ch-prog-bar{height:6px;background:rgba(0,0,0,.07);border-radius:4px;overflow:hidden}
|
||
.ch-prog-fill{height:100%;border-radius:4px;background:linear-gradient(90deg,var(--ch),var(--ch-d));transition:width .5s}
|
||
|
||
.ch-action{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-radius:11px;font-weight:700;font-size:.9rem;color:#fff;background:linear-gradient(135deg,var(--ch),var(--ch-d));transition:filter .15s}
|
||
.ch-action:hover{filter:brightness(1.08)}
|
||
|
||
.banner-soon{margin-top:18px;text-align:center;padding:20px;background:linear-gradient(135deg,var(--pri-soft),transparent);border:1px dashed var(--pri);border-radius:14px;color:var(--pri-d);font-weight:700;font-size:.92rem}
|
||
.banner-soon b{font-family:'Outfit',sans-serif;display:block;margin-bottom:4px;font-size:1.05rem}
|
||
|
||
.foot{text-align:center;padding:24px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="hdr">
|
||
<div class="hdr-inner">
|
||
<div>
|
||
<a href="/textbooks" class="hdr-back">
|
||
<svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
|
||
К каталогу
|
||
</a>
|
||
</div>
|
||
<div>
|
||
<h1>Физика — 11 класс</h1>
|
||
<div class="hdr-sub">Полный курс физики 11 класса · 8 глав · 45 параграфов</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
|
||
<section class="prog-overall">
|
||
<div class="po-icon">∿</div>
|
||
<div class="po-text">
|
||
<div class="po-label">Общий прогресс по курсу</div>
|
||
<div id="overall-text" style="font-size:1.05rem;font-weight:700">Загрузка...</div>
|
||
<div class="po-bar"><div id="overall-fill" class="po-fill" style="width:0%"></div></div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="ch-grid">
|
||
${cards}
|
||
</div>
|
||
|
||
<div class="banner-soon">
|
||
<b>Курс в активной разработке (W0)</b>
|
||
Инфраструктура готова: миграция БД, библиотека phys-fx.js (Oscillogram, SpringMass, Pendulum) и 8 stub-страниц глав. Реализация по плану PLAN_FIZIKA_11.md — 15 волн (~26 сессий).
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<footer class="foot">
|
||
Интерактивный учебник «Физика — 11 класс» · LearnSpace
|
||
</footer>
|
||
|
||
<script>
|
||
'use strict';
|
||
var TOTAL = 45;
|
||
var CH_PARA = {${CHAPTERS.map(c => "'" + c.slug + "': " + c.paras.length).join(', ')}};
|
||
var CH_IDX = {${CHAPTERS.map(c => "'" + c.slug + "': " + c.n).join(', ')}};
|
||
|
||
function setChProg(idx, readCount, total){
|
||
var pct = total ? Math.round(readCount * 100 / total) : 0;
|
||
var labelEl = document.getElementById('prog-' + idx);
|
||
var fillEl = document.getElementById('fill-' + idx);
|
||
var btnEl = document.getElementById('btn-' + idx);
|
||
if (labelEl) labelEl.textContent = pct + '%';
|
||
if (fillEl) fillEl.style.width = pct + '%';
|
||
if (btnEl){
|
||
if (readCount > 0 && readCount < total) btnEl.textContent = 'Продолжить';
|
||
else if (readCount >= total) btnEl.textContent = 'Открыть снова';
|
||
else btnEl.textContent = 'Открыть главу';
|
||
}
|
||
}
|
||
|
||
function renderProgress(children){
|
||
var totalRead = 0;
|
||
for (var i = 0; i < children.length; i++){
|
||
var ch = children[i];
|
||
var idx = CH_IDX[ch.slug]; if (!idx) continue;
|
||
var read = ch.progress ? ch.progress.read.length : 0;
|
||
var total = ch.para_count || CH_PARA[ch.slug] || 1;
|
||
totalRead += read;
|
||
setChProg(idx, read, total);
|
||
}
|
||
var pct = Math.round(totalRead * 100 / TOTAL);
|
||
var overallEl = document.getElementById('overall-text');
|
||
var fillEl = document.getElementById('overall-fill');
|
||
if (overallEl) overallEl.textContent = totalRead + ' из ' + TOTAL + ' параграфов · ' + pct + '%';
|
||
if (fillEl) fillEl.style.width = pct + '%';
|
||
}
|
||
|
||
function loadProgress(){
|
||
if (typeof window.LS === 'undefined' || typeof window.LS.api !== 'function'){
|
||
renderProgress([]); return;
|
||
}
|
||
window.LS.api('/api/textbooks/physics-11/children')
|
||
.then(function(data){
|
||
if (data && data.children) renderProgress(data.children);
|
||
else renderProgress([]);
|
||
})
|
||
.catch(function(){ renderProgress([]); });
|
||
}
|
||
|
||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', loadProgress);
|
||
else loadProgress();
|
||
window.addEventListener('focus', loadProgress);
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|
||
`;
|
||
}
|
||
|
||
/* Write all 9 files */
|
||
fs.writeFileSync(path.join(OUT, 'physics_11_hub.html'), makeHub(), 'utf8');
|
||
console.log('Wrote: physics_11_hub.html');
|
||
CHAPTERS.forEach(c => {
|
||
const fname = 'physics_11_ch' + c.n + '.html';
|
||
fs.writeFileSync(path.join(OUT, fname), makeChapter(c), 'utf8');
|
||
console.log('Wrote:', fname);
|
||
});
|
||
console.log('Done. 9 stub files generated.');
|