0284611263
- Миграция 027: textbooks hub geometry-10 + 4 ребёнка (r1 blue, r2 emerald, r3 rose, r4 amber) - frontend/js/stereo3d.js: библиотека 3D-проекций (Scene, CABINET/ISOMETRIC, cube/box/prism/pyramid/tetrahedron/plane/arrow/angle, авто-видимость рёбер) - geometry_10_hub.html: 4 карточки разделов, общий прогресс, превью 4 тел через stereo3d - 4 stub-файла разделов (r1-r4) с list параграфов и плашкой 'в разработке' - backend/scripts/gen_geom10_stubs.js: генератор stub-файлов
225 lines
12 KiB
JavaScript
225 lines
12 KiB
JavaScript
#!/usr/bin/env node
|
||
'use strict';
|
||
// Генератор stub-файлов разделов Геометрии 10. W0.
|
||
// Запуск: node backend/scripts/gen_geom10_stubs.js
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const sections = [
|
||
{ file:'geometry_10_r1.html', num:1, slug:'geometry-10-r1',
|
||
title:'Введение в стереометрию',
|
||
sub:'Пространственные фигуры · Аксиомы · Сечения',
|
||
range:'§1–§3 + Финал', wm:'△',
|
||
primary:'#2563eb', primaryD:'#1d4ed8', soft:'#dbeafe', dark:'#1e3a8a',
|
||
paras:[
|
||
{ n:1, title:'Пространственные фигуры',
|
||
sub:'Призма, пирамида, цилиндр, конус, шар. Грани, рёбра, вершины.' },
|
||
{ n:2, title:'Прямые и плоскости',
|
||
sub:'Аксиомы стереометрии (A1–A3) и их следствия. 4 способа задания плоскости.' },
|
||
{ n:3, title:'Построения сечений',
|
||
sub:'Метод следов. Сечения куба, призмы, пирамиды.' }
|
||
] },
|
||
{ file:'geometry_10_r2.html', num:2, slug:'geometry-10-r2',
|
||
title:'Параллельность',
|
||
sub:'Прямые · Прямая и плоскость · Плоскости',
|
||
range:'§4–§6 + Финал', wm:'∥',
|
||
primary:'#059669', primaryD:'#047857', soft:'#d1fae5', dark:'#064e3b',
|
||
paras:[
|
||
{ n:4, title:'Расположение прямых в пространстве',
|
||
sub:'Пересекающиеся, параллельные, скрещивающиеся прямые. Угол между скрещивающимися.' },
|
||
{ n:5, title:'Прямая и плоскость',
|
||
sub:'Три случая. Признак параллельности прямой и плоскости.' },
|
||
{ n:6, title:'Две плоскости',
|
||
sub:'Пересекаются или параллельны. Признак параллельности плоскостей.' }
|
||
] },
|
||
{ file:'geometry_10_r3.html', num:3, slug:'geometry-10-r3',
|
||
title:'Перпендикулярность',
|
||
sub:'Прямая ⊥ плоскость · Расстояния · Углы · Двугранный угол',
|
||
range:'§7–§10 + Финал', wm:'⊥',
|
||
primary:'#e11d48', primaryD:'#be123c', soft:'#fee2e2', dark:'#7f1d1d',
|
||
paras:[
|
||
{ n:7, title:'Перпендикулярность прямой и плоскости',
|
||
sub:'Определение, признак перпендикулярности.' },
|
||
{ n:8, title:'Расстояния',
|
||
sub:'От точки до плоскости, между параллельными плоскостями, между скрещивающимися.' },
|
||
{ n:9, title:'Угол между прямой и плоскостью',
|
||
sub:'Наклонная и её проекция. Теорема о трёх перпендикулярах.' },
|
||
{ n:10, title:'Перпендикулярность плоскостей',
|
||
sub:'Двугранный угол. Признак перпендикулярности плоскостей.' }
|
||
] },
|
||
{ file:'geometry_10_r4.html', num:4, slug:'geometry-10-r4',
|
||
title:'Координаты и векторы',
|
||
sub:'ПДСК в пространстве · Векторы · Скалярное произведение',
|
||
range:'§11–§14 + Финал', wm:'→',
|
||
primary:'#d97706', primaryD:'#b45309', soft:'#fef3c7', dark:'#78350f',
|
||
paras:[
|
||
{ n:11, title:'Координаты в пространстве',
|
||
sub:'ПДСК. Точка (x; y; z). Расстояние между точками.' },
|
||
{ n:12, title:'Вектор. Действия над векторами',
|
||
sub:'Сложение, умножение на число, базис i, j, k. Разложение вектора.' },
|
||
{ n:13, title:'Скалярное произведение',
|
||
sub:'a · b = |a||b|cos α = x₁x₂ + y₁y₂ + z₁z₂. Условие перпендикулярности.' },
|
||
{ n:14, title:'Применение координат и векторов',
|
||
sub:'Уравнения, углы, расстояния. Векторно-координатный метод.' }
|
||
] }
|
||
];
|
||
|
||
function html(s){
|
||
const parasHtml = s.paras.map(p => `
|
||
<article class="para-card" data-para="${p.n}">
|
||
<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>
|
||
Будет добавлено в следующей волне реализации
|
||
</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>Геометрия 10 · ${s.title}</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"></script>
|
||
<script src="/js/api.js" defer></script>
|
||
<script src="/js/xp.js" defer></script>
|
||
<script src="/js/stereo3d.js?v=1" defer></script>
|
||
<style>
|
||
:root{
|
||
--bg:#f8fafc; --card:#fff;
|
||
--text:#0f172a; --muted:#475569;
|
||
--border:#e2e8f0;
|
||
--pri:${s.primary}; --pri-d:${s.primaryD};
|
||
--pri-soft:${s.soft};
|
||
--dark:${s.dark};
|
||
--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,var(--dark) 0%,var(--pri) 55%,var(--pri-soft) 100%);color:#fff;padding:32px 24px 28px;overflow:hidden}
|
||
.hdr::before{content:'${s.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}
|
||
.hdr-side{margin-left:auto;display:flex;gap:8px}
|
||
.hdr-btn{padding:8px 12px;background:rgba(255,255,255,.14);border:none;color:#fff;border-radius:9px;cursor:pointer;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;font-family:inherit}
|
||
.hdr-btn:hover{background:rgba(255,255,255,.24)}
|
||
.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/geometry-10" class="hdr-back">
|
||
<svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
|
||
К курсу геометрии 10
|
||
</a>
|
||
</div>
|
||
<div>
|
||
<h1>Раздел ${s.num}. ${s.title}</h1>
|
||
<div class="hdr-sub">${s.sub} · ${s.range}</div>
|
||
</div>
|
||
<div class="hdr-side">
|
||
<button id="theme-btn" class="hdr-btn" title="Сменить тему">
|
||
<svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
|
||
<span id="theme-lab">Тёмная</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
|
||
<div class="intro-card">
|
||
<span class="intro-num">Раздел ${s.num}</span>
|
||
<h2>${s.title}</h2>
|
||
<p>${s.sub}. Раздел содержит ${s.paras.length} параграф${s.paras.length===1?'':(s.paras.length<5?'а':'ов')} и финальный этап с боссами.</p>
|
||
</div>
|
||
|
||
<div class="para-grid">
|
||
${parasHtml}
|
||
</div>
|
||
|
||
<div class="banner-soon">
|
||
<b>Раздел в разработке.</b> Полная реализация — в следующих волнах. Уже доступна 3D-библиотека <code>stereo3d.js</code>.
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<footer class="foot">
|
||
Геометрия — 10 класс · Раздел ${s.num} · LearnSpace
|
||
</footer>
|
||
|
||
<script>
|
||
'use strict';
|
||
(function(){
|
||
var saved = localStorage.getItem('geometry10_theme') || localStorage.getItem('theme') || 'light';
|
||
if (saved === 'dark') document.documentElement.classList.add('dark');
|
||
var lab = document.getElementById('theme-lab');
|
||
if (lab) lab.textContent = saved === 'dark' ? 'Светлая' : 'Тёмная';
|
||
document.getElementById('theme-btn').addEventListener('click', function(){
|
||
document.documentElement.classList.toggle('dark');
|
||
var dark = document.documentElement.classList.contains('dark');
|
||
localStorage.setItem('geometry10_theme', dark ? 'dark' : 'light');
|
||
localStorage.setItem('theme', dark ? 'dark' : 'light');
|
||
if (lab) lab.textContent = dark ? 'Светлая' : 'Тёмная';
|
||
});
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|
||
`;
|
||
}
|
||
|
||
const outDir = path.join(__dirname, '..', '..', 'frontend', 'textbooks');
|
||
for (const s of sections){
|
||
const fp = path.join(outDir, s.file);
|
||
fs.writeFileSync(fp, html(s), 'utf8');
|
||
console.log('Wrote:', fp);
|
||
}
|
||
console.log('Done.');
|