diff --git a/backend/scripts/redesign_p8_phase0.cjs b/backend/scripts/redesign_p8_phase0.cjs new file mode 100644 index 0000000..6fdf182 --- /dev/null +++ b/backend/scripts/redesign_p8_phase0.cjs @@ -0,0 +1,109 @@ +// Phase 0 — подключает design system + interactives CSS + новые JS-модули +// (phys8-anim.js, phys8-drag.js, phys8-helpers.js) во все 5 файлов учебника Физики 8: +// physics_8_hub.html, physics_8_ch1.html, physics_8_ch2.html, physics_8_ch3.html, physics_8_lab.html. +// Также добавляет body class (p8-theme-thermal/electric/spectrum) на каждой странице. +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const TBOOKS = path.join(__dirname, '..', '..', 'frontend', 'textbooks'); + +const FILES = [ + { name: 'physics_8_hub.html', theme: null }, + { name: 'physics_8_ch1.html', theme: 'thermal' }, + { name: 'physics_8_ch2.html', theme: 'electric' }, + { name: 'physics_8_ch3.html', theme: 'spectrum' }, + { name: 'physics_8_lab.html', theme: null }, +]; + +const CSS_LINKS = [ + '', + '', +]; + +const JS_LINKS = [ + '', + '', + '', +]; + +// Anchor: после katex link мы добавляем design-system css +const CSS_ANCHOR = ''; + +// Anchor: после phys.js / xp.js мы добавляем новые JS +const JS_ANCHOR_OPTIONS = [ + '', + '', +]; + +let totalPatched = 0; + +for (const { name, theme } of FILES) { + const fp = path.join(TBOOKS, name); + if (!fs.existsSync(fp)) { console.warn('miss:', name); continue; } + let h = fs.readFileSync(fp, 'utf8'); + const before = h.length; + let changes = []; + + // 1. CSS links + for (const link of CSS_LINKS) { + if (h.includes(link)) continue; + if (h.includes(CSS_ANCHOR)) { + h = h.replace(CSS_ANCHOR, CSS_ANCHOR + '\n' + link); + changes.push('+css: ' + link.match(/href="([^"]+)"/)[1]); + } + } + + // 2. JS links — место подключения: после любого из якорей + for (const link of JS_LINKS) { + if (h.includes(link)) continue; + let placed = false; + for (const anchor of JS_ANCHOR_OPTIONS) { + if (h.includes(anchor)) { + h = h.replace(anchor, anchor + '\n' + link); + placed = true; + changes.push('+js: ' + link.match(/src="([^"]+)"/)[1]); + break; + } + } + if (!placed) { + // Fallback: перед + h = h.replace('', link + '\n'); + changes.push('+js (head): ' + link.match(/src="([^"]+)"/)[1]); + } + } + + // 3. Theme class на body + if (theme) { + const themeClass = 'p8-theme-' + theme; + if (!h.includes(themeClass)) { + // Найти
и добавить класс + h = h.replace(/]*)>/, (match, attrs) => { + if (/class="([^"]*)"/.test(attrs)) { + return ' + `class="${cls} ${themeClass}"`) + '>'; + } + return ``; + }); + changes.push('+body class: ' + themeClass); + } + } + + fs.writeFileSync(fp, h); + if (changes.length) { + console.log(`${name}: ${before} → ${h.length} bytes`); + changes.forEach(c => console.log(' ' + c)); + totalPatched++; + } else { + console.log(`${name}: no changes (already patched)`); + } + + // Sanity parse inline scripts + const scripts = [...h.matchAll(/ + + + - +