diff --git a/frontend/textbooks/algebra_8_ch2.html b/frontend/textbooks/algebra_8_ch2.html index cb5f2c1..0491f89 100644 --- a/frontend/textbooks/algebra_8_ch2.html +++ b/frontend/textbooks/algebra_8_ch2.html @@ -266,6 +266,11 @@ input,select,textarea{font-family:inherit} .eq-show{font-family:'JetBrains Mono',monospace} .pipe-tabs .btn.active{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))} +/* XP badge in hero */ +.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;letter-spacing:.02em;box-shadow:0 4px 12px rgba(233,30,99,.22);font-family:'Unbounded',sans-serif} +.hero-xp-badge svg{flex-shrink:0} +.xp-card .hp-bar{box-shadow:inset 0 1px 2px rgba(0,0,0,.05)} + /* GLOSSARY tooltip */ .gloss-term{border-bottom:1.5px dotted var(--sec-acc,var(--pri));cursor:help;color:var(--sec-acc-d,var(--pri2));font-weight:600;padding:0 1px} .gloss-term:hover{background:var(--sec-acc-soft,var(--pri-soft));border-radius:3px} @@ -360,6 +365,7 @@ input,select,textarea{font-family:inherit}
0% +
@@ -526,6 +532,15 @@ function refreshProgressUI(){ const fl = el.querySelector('.psel-prog-fill'); if(fl) fl.style.width = (STATE.progress[k]||0) + '%'; }); + const xpBadge = document.getElementById('hero-xp-badge'); + if(xpBadge){ + const lv = levelFromXp(STATE.xp || 0); + xpBadge.innerHTML = ' Lv ' + lv + ' · ' + (STATE.xp || 0) + ' XP'; + } + // sidebar XP card sync + if(STATE.current && document.getElementById('sidebar-content')){ + try { buildSidebar(STATE.current); } catch(e){} + } } function achievement(id, text){ if(STATE.achievements.has(id)) return; @@ -636,16 +651,62 @@ const SIDEBARS = { ['Дробное','умножить на ОЗ, проверить ОДЗ'], ['ОДЗ','знаменатель $\\neq 0$'], ]}, - final2:{ title:'Финал', rows:[['Итоги главы','будет в Wave 4']]}, + final2:{ title:'Финал главы', rows:[ + ['7 боссов','один на каждый параграф + общий'], + ['Тип задач','select / yes-no / input'], + ['Награда','«Магистр квадратных уравнений»'], + ['Практика','случайные задачи всей главы'], + ['Серия 5×','+ достижение «Серия из 5 верных»'], + ]}, }; +const TIPS = [ + { sec:'p7', html:'Если в неполном ax² + c = 0 знаки a и c одинаковые — корней нет. Проверяй знак $-c/a$.' }, + { sec:'p8', html:'$D = b^2 - 4ac$ — даже не считай корни, если $D < 0$.' }, + { sec:'p9', html:'Перед использованием Виета убедись, что a = 1. Иначе формулы дают $-b/a$ и $c/a$.' }, + { sec:'p10', html:'Если $D < 0$, на множители первой степени не раскладывается. Не пытайся!' }, + { sec:'p11', html:'В задачах на движение часто помогает «общее время»: $\\dfrac{s_1}{v_1} + \\dfrac{s_2}{v_2}$.' }, + { sec:'p12', html:'После $t = x^2$ всегда проверяй $t \\geq 0$ — отрицательные $t$ дают пустое множество.' }, + { sec:'final2', html:'Не бойся пробовать боссов несколько раз: ошибка не «съедает» прогресс.' }, +]; +function levelFromXp(xp){ return Math.floor(Math.sqrt(xp / 50)); } +function xpForLevel(lv){ return 50 * lv * lv; } + function buildSidebar(id){ const box = document.getElementById('sidebar-content'); const sb = SIDEBARS[id] || SIDEBARS.p7; - let html = `

${sb.title}

`; + let html = ''; + + // XP card + const xp = STATE.xp || 0; + const lv = levelFromXp(xp); + const cur = xpForLevel(lv); + const next = xpForLevel(lv + 1); + const pct = next > cur ? Math.round((xp - cur) / (next - cur) * 100) : 100; + html += `
+

Опыт Lv ${lv}

+
${xp} XP${next} XP
+
+
До Lv ${lv + 1}: ${Math.max(0, next - xp)} XP
+
`; + + // Шпаргалка + html += `

${sb.title}

`; sb.rows.forEach(([k,v])=>{ html += `
${k} ${v ? '— ' + v : ''}
`; }); html += '
'; + + // Совет дня + const tip = TIPS.find(t => t.sec === id) || TIPS[0]; + html += `
+

+ + Подсказка +

+
${tip.html}
+
`; + + // Достижения if(STATE.achievements.size > 0){ html += `

Достижения ${STATE.achievements.size}

`; [...STATE.achievements.values()].slice(-4).forEach(text=>{ @@ -653,6 +714,7 @@ function buildSidebar(id){ }); html += '
'; } + box.innerHTML = html; if(window.renderMathInElement) try{ renderMath(box); }catch(e){} }