// Phase 1 — визуальный редизайн ch1 (Тепловые явления): // 1. Hero: заменяет старый
на p8-hero с // eyebrow, title, sub, live meter, watermark (огонь SVG). // 2. Section watermarks: в каждом
добавляет // тематический SVG-watermark (пламя/термометр/снежинка). // 3. Inject IV-6 (drag-thermometer) в §1 — flagship интерактив. // Остальные §2-11 получают IV-6 stub-placeholder с заголовком. 'use strict'; const fs = require('fs'); const path = require('path'); const DST = path.join(__dirname, '..', '..', 'frontend', 'textbooks', 'physics_8_ch1.html'); let h = fs.readFileSync(DST, 'utf8'); // === 1. Replace .hdr block with p8-hero === const FIRE_WM = ``; const NEW_HERO = `
${FIRE_WM}
37°C
Глава 1 · 11 параграфов

Тепловые явления

Внутренняя энергия, способы теплопередачи, плавление и кипение. Перетаскивайте термометры, нагреватели и материалы — наблюдайте поведение тепла в реальном времени.
К физике 8
`; const oldHdrRegex = /
[\s\S]*?<\/header>/; if (h.match(oldHdrRegex)) { h = h.replace(oldHdrRegex, NEW_HERO); console.log('Hero replaced'); } // === 2. Update meter live: добавим скрипт, который анимирует значение в углу === const METER_SCRIPT = ` `; if (!h.includes('P8 hero meter')) { h = h.replace('', METER_SCRIPT + '\n'); console.log('Meter animation script added'); } // === 3. Section watermarks — добавить data-attribute, CSS подхватит === // Используем pseudo-element через inline стиль не получится; вместо этого // инжектим
в каждую секцию. // Watermark SVG-символы по § const SEC_SYMBOLS = { p1: '', // атом p2: '', // вверх/вниз p3: '', // правая стрелка p4: '', // спирали (конвекция) p5: '', // солнце p6: '', // сосуд p7: '', // пламя/огонь p8: '', // фазовый переход p9: '', // ромб p10: '', // капля + пар p11: '', // пузыри }; let secWmInjected = 0; for (const pid of Object.keys(SEC_SYMBOLS)) { const symbol = SEC_SYMBOLS[pid]; const secOpenRegex = new RegExp(`(]+id="sec-${pid}"[^>]*>)`); if (h.match(secOpenRegex) && !h.includes(`p8-sec-wm-${pid}`)) { const wmDiv = ``; h = h.replace(secOpenRegex, '$1\n ' + wmDiv); secWmInjected++; } } console.log('Section watermarks injected:', secWmInjected); // Обеспечиваем что section имеет position:relative — добавляем inline-стиль // (или полагаемся на существующий CSS .sec, который позиционирован) // Проверим: ищем `.sec{` в style // (Это уже есть в существующем CSS, sec — позиционирован относительно) // === 4. IV-6 flagship: для §1 добавляем drag-thermometer интерактив === // Patch build_p1 — добавим IV-6 widget HTML + _initP1_iv6 функцию. const IV6_P1_WIDGET = ` /* IV6 — Drag thermometer (Phase 1 flagship interactive) */ h += '
' +'
IV-6
Перетащи термометр
' +'
Перетащи термометр на одно из четырёх тел и наблюдай, как меняется его температурный отсчёт. Тела разной массы и при разных условиях — оцени, в каком из них больше внутренней энергии.
' +'
' +'
' +'
T°C
' +'
U отн.
' +'
' +'
'; `; const IV6_P1_INIT = ` function _initP1_iv6(){ const sandbox = document.getElementById('p1-iv6-sandbox'); if (!sandbox || !window.P8Helpers || !window.P8Drag) return; const svg = P8Helpers.svg.create(560, 320); svg.setAttribute('width', '100%'); svg.setAttribute('height', '100%'); svg.style.display = 'block'; sandbox.appendChild(svg); /* 4 тела: имя, T (°C), относительная U */ const bodies = [ { name:'Лёд 1 кг', cx: 95, cy: 200, T: -10, U: 14, color:'#bfdbfe' }, { name:'Вода 1 кг', cx: 230, cy: 200, T: 20, U: 100, color:'#7dd3fc' }, { name:'Чай 0,3 кг',cx: 365, cy: 200, T: 80, U: 80, color:'#fb923c' }, { name:'Пар 0,5 кг',cx: 500, cy: 200, T: 110, U: 200, color:'#ef4444' } ]; bodies.forEach(b => { const g = P8Helpers.svg.el('g', { transform: 'translate('+b.cx+','+b.cy+')' }); g.appendChild(P8Helpers.svg.el('rect', { x: -50, y: -55, width: 100, height: 110, rx: 12, fill: b.color, stroke: '#0f172a', 'stroke-width': 1.5, opacity: 0.88 })); g.appendChild(P8Helpers.svg.el('text', { x: 0, y: -68, 'font-family': "'JetBrains Mono', monospace", 'font-size': 11, 'font-weight': 700, fill: '#0f172a', 'text-anchor': 'middle', text: b.name })); g.dataset = b; svg.appendChild(g); }); /* Термометр (draggable group) */ let thermoX = 50, thermoY = 70; const thermoG = P8Helpers.svg.el('g', { transform: 'translate('+thermoX+','+thermoY+')', 'class': 'p8-draggable' }); /* Drop shadow rect */ thermoG.appendChild(P8Helpers.svg.el('rect', { x: -22, y: -10, width: 44, height: 130, fill: 'transparent' })); /* Tube */ thermoG.appendChild(P8Helpers.svg.el('rect', { x: -5, y: 0, width: 10, height: 100, rx: 5, fill: '#f3f4f6', stroke: '#475569', 'stroke-width': 1.5 })); const fill = P8Helpers.svg.el('rect', { x: -3, y: 70, width: 6, height: 30, rx: 2, fill: '#f97316' }); thermoG.appendChild(fill); /* Bulb */ const bulb = P8Helpers.svg.el('circle', { cx: 0, cy: 110, r: 12, fill: '#f97316', stroke: '#475569', 'stroke-width': 1.5 }); thermoG.appendChild(bulb); thermoG.appendChild(P8Helpers.svg.el('text', { x: 0, y: -2, 'font-family': "'Inter', sans-serif", 'font-size': 10, 'font-weight': 700, fill: '#0f172a', 'text-anchor': 'middle', text: 'Drag' })); svg.appendChild(thermoG); /* Show current readout */ const tEl = document.getElementById('p1-iv6-T'); const uEl = document.getElementById('p1-iv6-U'); function checkHit(cx, cy){ for (const b of bodies){ if (Math.abs(cx - b.cx) < 50 && Math.abs(cy - b.cy) < 55){ const tColor = P8Helpers.thermal.tempColor((b.T + 20) / 130); fill.setAttribute('fill', tColor); bulb.setAttribute('fill', tColor); if (tEl) tEl.textContent = b.T; if (uEl) uEl.textContent = b.U; return b; } } fill.setAttribute('fill', '#94a3b8'); bulb.setAttribute('fill', '#94a3b8'); if (tEl) tEl.textContent = '—'; if (uEl) uEl.textContent = '—'; return null; } P8Drag.attach(thermoG, { container: svg, onMove: (ev, pos) => { thermoX = Math.max(20, Math.min(540, pos.x)); thermoY = Math.max(10, Math.min(200, pos.y)); thermoG.setAttribute('transform', 'translate('+thermoX+','+thermoY+')'); checkHit(thermoX, thermoY + 110); } }); if (window.addXp) { setTimeout(() => addXp(5, 'p1-iv6-explore'), 12000); } } `; const insertMarker = `box.innerHTML = h + secNavFor('p1') + readButton('p1');`; if (!h.includes('p1-iv6-sandbox') && h.includes(insertMarker)) { h = h.replace(insertMarker, IV6_P1_WIDGET.trim() + '\n\n ' + insertMarker); // Add init call after wireReadBtn h = h.replace(`wireReadBtn('p1');`, `wireReadBtn('p1');\n _initP1_iv6();`); // Append init function after build_p1 const p1Start = h.indexOf('function build_p1()'); const p1End = h.indexOf('\n}\n', p1Start); h = h.slice(0, p1End + 3) + '\n' + IV6_P1_INIT.trim() + '\n' + h.slice(p1End + 3); console.log('IV-6 §1 drag-thermometer injected'); } // === 5. Stub IV-6 placeholders для §2-11 === for (let n = 2; n <= 11; n++) { const pid = 'p' + n; const stubHtml = ` /* IV6 — flagship интерактив (заглушка Phase 1, наполнение в Phase 1.${n}) */ h += '
' +'
IV-6
Новый интерактив §${n}
' +'
Готовится: интерактивная визуализация с drag-and-drop для углубления темы. Скоро будет доступна.
' +'
' +'' +'
Phase 1.${n} — coming soon
' +'
' +'
'; `; const marker = `box.innerHTML = h + secNavFor('${pid}') + readButton('${pid}');`; if (!h.includes(`p8-iv6-${pid}`) && !h.includes(`Новый интерактив §${n}`) && h.includes(marker)) { h = h.replace(marker, stubHtml.trim() + '\n\n ' + marker); console.log(` ${pid}: IV-6 stub added`); } } fs.writeFileSync(DST, h); console.log('ch1 final size:', h.length); // Sanity parse const scripts = [...h.matchAll(/