diff --git a/backend/scripts/migrate_phys9_tasks.cjs b/backend/scripts/migrate_phys9_tasks.cjs new file mode 100644 index 0000000..cdad699 --- /dev/null +++ b/backend/scripts/migrate_phys9_tasks.cjs @@ -0,0 +1,94 @@ +// Inject task panels (ptab-pN scaffold + JS auto-render) into physics_9_ch4.html +// for §31..§36 — the only paragraphs with TASKS_PN arrays in the monolith. +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const TBOOKS = path.join(__dirname, '..', '..', 'frontend', 'textbooks'); +const SRC = path.join(TBOOKS, 'physics_9.html'); +const DST = path.join(TBOOKS, 'physics_9_ch4.html'); + +const src = fs.readFileSync(SRC, 'utf8'); +let ch4 = fs.readFileSync(DST, 'utf8'); + +// === Extract each ptab-pN block (N in 31..36) === +function clean(s) { + return s + .replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{27BF}]|[\u{1F000}-\u{1F2FF}]|[\u{FE0F}]/gu, '') + .replace(/]*>\s*<\/i>/g, '') + .trim(); +} + +function extractPtab(n) { + const tag = `id="ptab-p${n}"`; + const i = src.indexOf(tag); + if (i < 0) return null; + // Откатываемся к открывающему div + const divStart = src.lastIndexOf(' непосредственно перед следующим ptab + let closingDiv = src.lastIndexOf('', endTag); + if (closingDiv < divStart) closingDiv = endTag; + return src.slice(divStart, closingDiv + 6); +} + +const PTABS = {}; +for (let n = 31; n <= 36; n++) { + const b = extractPtab(n); + if (b) PTABS[n] = clean(b); +} +console.log('Extracted ptabs:', Object.keys(PTABS).map(k => `p${k}:${PTABS[k].length}b`).join(' ')); + +// === Inject ptab block + auto-render call into each build_pN in ch4 === +// Pattern: each build_pN ends with `box.innerHTML = html; renderMath(box); wireReadBtn('pN');` +// We append the ptab HTML to the same box, then call window.goToTask('pN', 0) to start. + +for (let n = 31; n <= 36; n++) { + if (!PTABS[n]) continue; + const pid = 'p' + n; + // Escape for template literal + const esc = PTABS[n].replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${'); + + // Find build_pN function end + const fnRegex = new RegExp( + `(function\\s+build_${pid}\\(\\)\\s*\\{[\\s\\S]*?wireReadBtn\\('${pid}'\\);)\\s*\\}`, + 'm' + ); + const match = ch4.match(fnRegex); + if (!match) { + console.warn(`build_${pid}: not found`); + continue; + } + + // Build new function body: append ptab HTML + setup call + const injectedBlock = ` + // === Задачи §${n} — task panel (auto-injected from monolith) === + const tasksBlock = document.createElement('div'); + tasksBlock.className = 'wg'; + tasksBlock.style.marginTop = '20px'; + tasksBlock.innerHTML = '
Задачи
Тренажёр §${n}
' + \`${esc}\`; + box.appendChild(tasksBlock); + // Auto-render first task + setTimeout(() => { + try { if (typeof goToTask === 'function') goToTask('${pid}', 0); } + catch(e) { console.warn('${pid} goToTask:', e.message); } + }, 80);`; + + const newBody = match[1] + injectedBlock + '\n}'; + ch4 = ch4.replace(fnRegex, () => newBody); + console.log(` build_${pid}: injected ptab (${esc.length} bytes)`); +} + +fs.writeFileSync(DST, ch4); +console.log('ch4 size:', ch4.length); + +// Sanity parse inline scripts +const scripts = [...ch4.matchAll(/