From 1f17fb40dc8b8def7009270d2a3629f3c00776b8 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Sat, 30 May 2026 09:16:24 +0300 Subject: [PATCH] =?UTF-8?q?fix(textbooks):=20=D0=B8=D0=B7=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D1=87=D1=91=D0=BD=20=D0=BE=D0=B1=D1=89=D0=B8=D0=B9=20wid?= =?UTF-8?q?get=20CSS=20=E2=80=94=20phys-textbook-widgets.css?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Жалоба пользователя по Физике 8 (но проблема общая для Phys 8 и Phys 9): страницы глав используют классы .wg/.dnd-pool/.dnd-chip/.btn/.score-display/ .feedback/.actions/.sliders/.spoiler/.drop-box в HTML-разметке, но CSS-правила для них живут только в physics_10_ch1.html. Из-за этого карточки-задания, chip'ы drag-and-drop, кнопки и feedback-блоки в Phys 8 и Phys 9 рендерились без стилей (как обычный текст). - extract_widget_css.cjs: вытягивает CSS-блок (.btn..pre-.col-side) из physics_10_ch1.html в frontend/css/phys-textbook-widgets.css (6.4 КБ) - Подключает в 11 файлов: physics_8_ch1/ch2/ch3/hub/lab, physics_9_ch1..ch5, physics_9_hub - migrate_phys9_content.js теперь инжектит ссылку на widget CSS при будущих миграциях (рядом с FA CDN) --- backend/scripts/extract_widget_css.cjs | 54 +++++++++++++++++++++ backend/scripts/migrate_phys9_content.js | 8 ++- frontend/css/phys-textbook-widgets.css | 62 ++++++++++++++++++++++++ frontend/textbooks/physics_8_ch1.html | 1 + frontend/textbooks/physics_8_ch2.html | 1 + frontend/textbooks/physics_8_ch3.html | 1 + frontend/textbooks/physics_8_hub.html | 1 + frontend/textbooks/physics_8_lab.html | 1 + frontend/textbooks/physics_9_ch1.html | 1 + frontend/textbooks/physics_9_ch2.html | 1 + frontend/textbooks/physics_9_ch3.html | 1 + frontend/textbooks/physics_9_ch4.html | 1 + frontend/textbooks/physics_9_ch5.html | 1 + frontend/textbooks/physics_9_hub.html | 1 + 14 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 backend/scripts/extract_widget_css.cjs create mode 100644 frontend/css/phys-textbook-widgets.css diff --git a/backend/scripts/extract_widget_css.cjs b/backend/scripts/extract_widget_css.cjs new file mode 100644 index 0000000..cf230c7 --- /dev/null +++ b/backend/scripts/extract_widget_css.cjs @@ -0,0 +1,54 @@ +// Извлекает CSS виджетов (.wg, .dnd-pool, .btn, .feedback, .sliders, .score-display, .spoiler) +// из physics_10_ch1.html в frontend/css/phys-textbook-widgets.css. +// Подключается из всех physics_8_*.html и physics_9_*.html файлов. +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const SRC = path.join(__dirname, '..', '..', 'frontend', 'textbooks', 'physics_10_ch1.html'); +const DST_CSS = path.join(__dirname, '..', '..', 'frontend', 'css', 'phys-textbook-widgets.css'); + +const h = fs.readFileSync(SRC, 'utf8'); +const css = h.match(/', inject + ''); } - // FA CDN + // FA CDN + widget CSS if (!h.includes('font-awesome')) { h = h.replace( '', '\n' ); } + if (!h.includes('phys-textbook-widgets.css')) { + h = h.replace( + '', + '\n' + ); + } // phys9_legacy.js (provides startAnim1, lab11*, checkNum, togglePend36, etc.) if (!h.includes('phys9_legacy.js')) { diff --git a/frontend/css/phys-textbook-widgets.css b/frontend/css/phys-textbook-widgets.css new file mode 100644 index 0000000..077534b --- /dev/null +++ b/frontend/css/phys-textbook-widgets.css @@ -0,0 +1,62 @@ +/* Auto-extracted from frontend/textbooks/physics_10_ch1.html. + * Provides .wg, .dnd-pool, .dnd-chip, .drop-box, .btn, .feedback, + * .actions, .sliders, .score-display, .spoiler — shared interactive + * widget styles for all physics-N chapter pages. + * Generated by backend/scripts/extract_widget_css.cjs. + */ + +.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s} +.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))} +.btn:active{transform:scale(.96)} +.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))} +.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))} + +.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none} +.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)} +.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)} + +.boss-card{transition:border-color .35s,box-shadow .6s,background .3s,transform .2s;position:relative;overflow:hidden} +.boss-card.glow{box-shadow:0 0 24px rgba(16,185,129,.6),0 0 0 2px rgba(16,185,129,.45)!important} +@keyframes bossPulse{0%{box-shadow:0 0 0 0 rgba(16,185,129,.55)}70%{box-shadow:0 0 0 14px rgba(16,185,129,0)}100%{box-shadow:0 0 0 0 rgba(16,185,129,0)}} +.boss-card.pulse{animation:bossPulse .8s ease-out} + +.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1} +.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px} +.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em} +.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1} +.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px} +.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace} +.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))} +.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px} +.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px} +.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5} +.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px} +.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))} +.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px} +.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem} +.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden} +.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px} +.spoiler summary::-webkit-details-marker{display:none} +.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px} +.spoiler[open] summary::before{content:'\2212'} +.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6} + +.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s} +.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid} +.dnd-pool.col{flex-direction:column;align-items:stretch} +.dnd-pool.col .dnd-chip{width:auto} +.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%} +.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)} +.dnd-chip:active{cursor:grabbing} +.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(0,0,0,.10);transform:translateY(-1px)} +.dnd-chip.dragging{opacity:.28} +.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))} +.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer} +.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)} +.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s} +.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))} +.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em} +.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)} +.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px} +.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px} +.dnd-hint svg{width:14px;height:14px;flex-shrink:0} diff --git a/frontend/textbooks/physics_8_ch1.html b/frontend/textbooks/physics_8_ch1.html index 99a9f86..0709d87 100644 --- a/frontend/textbooks/physics_8_ch1.html +++ b/frontend/textbooks/physics_8_ch1.html @@ -8,6 +8,7 @@ Физика 8 · Глава 1 · «Тепловые явления» + diff --git a/frontend/textbooks/physics_8_ch2.html b/frontend/textbooks/physics_8_ch2.html index 9e0a59f..5d3c866 100644 --- a/frontend/textbooks/physics_8_ch2.html +++ b/frontend/textbooks/physics_8_ch2.html @@ -8,6 +8,7 @@ Физика 8 · Глава 2 · «Электромагнитные явления» + diff --git a/frontend/textbooks/physics_8_ch3.html b/frontend/textbooks/physics_8_ch3.html index 8107502..d42aa2c 100644 --- a/frontend/textbooks/physics_8_ch3.html +++ b/frontend/textbooks/physics_8_ch3.html @@ -8,6 +8,7 @@ Физика 8 · Глава 3 · «Световые явления» + diff --git a/frontend/textbooks/physics_8_hub.html b/frontend/textbooks/physics_8_hub.html index e7ce44c..69c1d8e 100644 --- a/frontend/textbooks/physics_8_hub.html +++ b/frontend/textbooks/physics_8_hub.html @@ -9,6 +9,7 @@ Физика 8 класс — учебник + diff --git a/frontend/textbooks/physics_8_lab.html b/frontend/textbooks/physics_8_lab.html index e47e4ab..984875b 100644 --- a/frontend/textbooks/physics_8_lab.html +++ b/frontend/textbooks/physics_8_lab.html @@ -8,6 +8,7 @@ Физика 8 · Лабораторный практикум + diff --git a/frontend/textbooks/physics_9_ch1.html b/frontend/textbooks/physics_9_ch1.html index e0dec44..007ab82 100644 --- a/frontend/textbooks/physics_9_ch1.html +++ b/frontend/textbooks/physics_9_ch1.html @@ -8,6 +8,7 @@ Физика 9 · Глава 1 · «Основы кинематики» +