fix(textbooks): Физика 9 — STATE collision, KaTeX escape, авто-init симуляций

Три бага из жалобы пользователя:

1) phys9_legacy.js упал с 'Identifier STATE has already been declared' —
   const STATE в монолите конфликтовал с const STATE в chapter inline JS.
   Скрипт extract_phys9_legacy.cjs теперь оборачивает извлечённый код в IIFE
   и явно экспортит через window 70 функций (upd*/draw*/init*/start*/lab*/
   check*/toggle*/render*/show*/...) + 7 const-массивов (TASKS_PN, PUZ_PN).

2) В боковой панели формулы рендерились как 'Delta vecr' вместо Δr⃗ —
   мой переход на JSON.stringify в gen_phys9_ch.js добавил лишний слой
   escape backslash. Уменьшил \\ → \ в SIDEBAR_ROWS, TIPS_HTML,
   PARA_SUBS, LR_SUBS (90 строк). Цепочка теперь: source \Delta → string
   \Delta → JSON "\\Delta" → HTML JS \Delta → runtime \Delta →
   KaTeX \Delta ✓.

3) 'не работают симуляции' — функции из legacy.js были доступны, но
   chapter goTo(id) их не вызывал. Добавлен авто-вызов upd<N>(),
   startAnim<N>(), init<N>(), draw<N>() при переключении на параграф,
   и updLab<N>(), drawLab<N>() — для ЛР.
This commit is contained in:
Maxim Dolgolyov
2026-05-30 09:06:20 +03:00
parent c26423b7d4
commit 66bd7ac1f4
8 changed files with 412 additions and 194 deletions
+29 -12
View File
@@ -15,7 +15,6 @@
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<script src="/js/phys.js" defer></script>
<script src="/js/phys9_legacy.js" defer></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
@@ -619,10 +618,10 @@ const _TB_SLUG = 'physics-9-ch3';
const PARAS = [
{ id:"p25", num:"§ 25", name:"Условия равновесия тел. Момент силы", sub:"$M = Fl$" },
{ id:"p26", num:"§ 26", name:"Простые механизмы. Рычаги. Блоки", sub:"$F_1 l_1 = F_2 l_2$" },
{ id:"p27", num:"§ 27", name:"Наклонная плоскость. «Золотое правило» механики. КПД", sub:"$\\\\eta = A_{пол}/A_{сов}$" },
{ id:"p27", num:"§ 27", name:"Наклонная плоскость. «Золотое правило» механики. КПД", sub:"$\\eta = A_{пол}/A_{сов}$" },
{ id:"p28", num:"§ 28", name:"Центр тяжести. Виды равновесия", sub:"ЦТ · равновесие" },
{ id:"p29", num:"§ 29", name:"Закон Архимеда. Выталкивающая сила", sub:"$F_A = \\\\rho g V$" },
{ id:"p30", num:"§ 30", name:"Плавание судов. Воздухоплавание", sub:"$\\\\rho_т \\\\le \\\\rho_ж$" },
{ id:"p29", num:"§ 29", name:"Закон Архимеда. Выталкивающая сила", sub:"$F_A = \\rho g V$" },
{ id:"p30", num:"§ 30", name:"Плавание судов. Воздухоплавание", sub:"$\\rho_т \\le \\rho_ж$" },
{ id:"final3", num:'\u2605', name:'Финал главы', sub:"Итоги · боссы главы 3", final:true }
];
PARAS.forEach(p => { STATE.progress[p.id] = 0; });
@@ -754,26 +753,44 @@ function goTo(id){
window.scrollTo({top:0,behavior:'smooth'});
if((STATE.progress[id]||0)<10) bumpProgress(id, 10);
if(window.renderMathInElement) setTimeout(()=>renderMath(el), 0);
// Auto-init legacy simulations: call upd<N>() / startAnim<N>() / draw<N>() if defined in phys9_legacy.js.
if(id.startsWith('p')){
const n = id.slice(1);
setTimeout(()=>{
['upd','startAnim','init','draw'].forEach(prefix=>{
const fn = window[prefix + n];
if(typeof fn === 'function'){ try{ fn(); }catch(e){ console.warn(prefix + n + ' init:', e.message); } }
});
}, 50);
} else if(id.startsWith('lr')){
const n = id.slice(2);
setTimeout(()=>{
['updLab','drawLab'].forEach(prefix=>{
const fn = window[prefix + n];
if(typeof fn === 'function'){ try{ fn(); }catch(e){} }
});
}, 50);
}
markLastPara(id);
}
const SIDEBARS = {
p25:{title:"Шпаргалка §25",rows:[["$M = Fl$","момент силы"],["$\\\\sum\\\\vec F = 0$ и $\\\\sum M = 0$",""],["Плечо","$l$ — расст. от оси до линии действия"]]},
p25:{title:"Шпаргалка §25",rows:[["$M = Fl$","момент силы"],["$\\sum\\vec F = 0$ и $\\sum M = 0$",""],["Плечо","$l$ — расст. от оси до линии действия"]]},
p26:{title:"Шпаргалка §26",rows:[["Рычаг","$F_1 l_1 = F_2 l_2$"],["Неподв. блок","без выигрыша"],["Подв. блок","выигрыш в силе в 2 раза"]]},
p27:{title:"Шпаргалка §27",rows:[["Накл. пл.","выигрыш = $l/h$"],["«Золотое правило»","выигр. в силе = проигр. в пути"],["$\\\\eta = A_{пол}/A_{сов}$","КПД"]]},
p27:{title:"Шпаргалка §27",rows:[["Накл. пл.","выигрыш = $l/h$"],["«Золотое правило»","выигр. в силе = проигр. в пути"],["$\\eta = A_{пол}/A_{сов}$","КПД"]]},
p28:{title:"Шпаргалка §28",rows:[["ЦТ","точка прилож. силы тяжести"],["Устойч.","ЦТ при отклонении поднимается"],["Безразл.","ЦТ не меняется"]]},
p29:{title:"Шпаргалка §29",rows:[["$F_A = \\\\rho g V_{погр}$",""],["Вверх","направление"],["Архимед","выталкивающая сила"]]},
p30:{title:"Шпаргалка §30",rows:[["Плав.","$\\\\rho_т \\\\le \\\\rho_ж$"],["Ватерлиния","граница погружения"],["Воздухопл.","подъёмная сила"]]},
p29:{title:"Шпаргалка §29",rows:[["$F_A = \\rho g V_{погр}$",""],["Вверх","направление"],["Архимед","выталкивающая сила"]]},
p30:{title:"Шпаргалка §30",rows:[["Плав.","$\\rho_т \\le \\rho_ж$"],["Ватерлиния","граница погружения"],["Воздухопл.","подъёмная сила"]]},
final3:{title:"Финал главы 3",rows:[["§§2530","теория главы 3"],["Награда","+50 XP"]]}
};
const TIPS=[
{sec:"p25",html:"Условия равновесия: $\\\\sum\\\\vec F = 0$ И $\\\\sum M = 0$. Момент силы $M = F \\\\cdot l$, где $l$ — плечо."},
{sec:"p25",html:"Условия равновесия: $\\sum\\vec F = 0$ И $\\sum M = 0$. Момент силы $M = F \\cdot l$, где $l$ — плечо."},
{sec:"p26",html:"Рычаг в равновесии: $F_1 l_1 = F_2 l_2$. Неподвижный блок выигрыша не даёт. Подвижный — выигрыш в силе в 2 раза."},
{sec:"p27",html:"Накл. плоскость: выигрыш в силе = $l/h$. «Золотое правило»: выигрываем в силе — проигрываем в пути. КПД: $\\\\eta = A_{пол}/A_{сов}$."},
{sec:"p27",html:"Накл. плоскость: выигрыш в силе = $l/h$. «Золотое правило»: выигрываем в силе — проигрываем в пути. КПД: $\\eta = A_{пол}/A_{сов}$."},
{sec:"p28",html:"Центр тяжести — точка приложения равнодействующей сил тяжести. Устойчивое равновесие: ЦТ при отклонении поднимается."},
{sec:"p29",html:"Закон Архимеда: $F_A = \\\\rho_ж g V_{погр}$. Направлен вверх. Не зависит от глубины, формы тела или плотности тела."},
{sec:"p30",html:"Условие плавания: $\\\\rho_т \\\\le \\\\rho_ж$. Подъёмная сила воздухоплавательного аппарата — разность веса вытесненного воздуха и веса аппарата."},
{sec:"p29",html:"Закон Архимеда: $F_A = \\rho_ж g V_{погр}$. Направлен вверх. Не зависит от глубины, формы тела или плотности тела."},
{sec:"p30",html:"Условие плавания: $\\rho_т \\le \\rho_ж$. Подъёмная сила воздухоплавательного аппарата — разность веса вытесненного воздуха и веса аппарата."},
{sec:"final3",html:"Финал главы 3 — интегрированные задачи по §§25–30. В разработке (Phase 3+)."}
];