Commit Graph

16 Commits

Author SHA1 Message Date
Maxim Dolgolyov 83f0ba9c04 fix(features): пустой блок флешкарт, лаба в сайдбаре, мигание (FOUC)
Три проблемы UX отключения модулей:
1) Пустой блок флешкарт на дашборде: виджет #w-flashcard — <div>, а скрытие шло
   только по [href]. Добавлена карта FEATURE_WIDGETS (флешкарты→#w-flashcard) —
   контейнер прячется целиком.
2) Лаборатория не уходила из сайдбара: не было ГЛОБАЛЬНОГО тумблера lab (только
   free-student). Добавлен в whitelist updateFeatures + GAME_FEATURES; map уже знал
   lab→/lab. Теперь выключение скрывает пункт и редиректит со страницы.
3) Мигание выключенных модулей (FOUC): hideDisabledFeatures асинхронный. Теперь
   loadFeatures кэширует /api/features в localStorage, а _applyFeatureCss инъектит
   <style id=ls-feat-hide> синхронно из кэша на ранней загрузке (api.js идёт до
   sidebar.js) — сайдбар/виджеты строятся уже скрытыми. Геймификация: класс
   no-gamification ставится на <html> (раньше body), ls.css правило body.no-gamification
   → .no-gamification (работает и для html, и для body).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:41:11 +03:00
Maxim Dolgolyov dc71d7b4d9 fix(gamification): полнота kill-switch — испытания/стрик/монеты + гейт счётчиков
Аудит выключателя геймификации выявил элементы, НЕ покрытые body.no-gamification:
испытания недели (#ch-section/.ch-widget), календарь стриков (.streak-cal),
стат-кольцо стрика (#sr-streak), монеты в профиле (#p-coins-row), чипы стрик/цель
на карточке питомца. Добавлены в CSS kill-switch (ls.css). Бэкенд: updateChallenges
и onLabExperiment писали прогресс/счётчики без проверки флага — добавлен гейт
isGamificationEnabled() (XP/coins/achievements уже гейтились в award*-функциях).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:04:30 +03:00
Maxim Dolgolyov e88cd431ca style(notifications): редизайн dropdown — иконки по типу, левый акцент у непрочитанных, sticky-шапка 2026-06-01 10:47:06 +03:00
Maxim Dolgolyov 79992d23c5 fix(bg): rain — drops instead of vertical stripes
Linear-gradients tiled at 3px wide produced striped curtains, not
rain. Switched to two pseudo-element layers of elongated radial
ellipses (1.5px × 12-18px) scattered across 130-180px tiles —
sparse drops at two depths with different fall speeds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 21:38:25 +03:00
Maxim Dolgolyov f6fbe922a9 feat(shop): 9 premium animated backgrounds
Doubles the bg catalogue from 10 to 19 with richer multi-layer
animations. Every keyframe pack is CSS-only and respects the existing
prefers-reduced-motion fallback.

  sunset       550   slow hue cycle through warm palette
  rain         650   2-layer vertical streaks at different speeds
  snow         700   3-layer drifting flakes pattern
  clouds       750   drifting white blobs on day sky (only LIGHT one)
  fireflies    800   pulsing glowing dots, opposing drift
  cyber-grid   850   neon grid scrolling down with vignette
  kaleidoscope 1000  two huge conic-gradients in opposite rotation
  ocean        1100  layered blobs drift like undulating waves
  aurora-dance 1500  multi-band aurora — new premium top-tier

Tonal classification mirrored in api.js DARK_BG_SLUGS so the veil
picks the right contrast: clouds is light, the other 8 join the dark
set (alongside dark, stars, aurora, nebula, grid).

Each background also gains a matching .bg-preview.bg-<slug> rule that
reuses the same animation at the shop's 90px swatch — WYSIWYG.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 21:35:59 +03:00
Maxim Dolgolyov d838c94df4 fix(bg): visible button-groups and tracks on dark backgrounds
The pill containers (.p-tabs, .shop-filters) used a 6% black fill that
disappeared on the dark veil, so the rounded button group lost its
outline and the inactive tabs looked like floating text. Same for the
xp / progress tracks (.ach-xp-progress, .ep-bar, .po-bar) that used
7% black.

Dark-tone overrides:
  • Containers get a 6% white wash + 10% white border so the pill
    shape stays readable
  • Inactive p-tab gets the same color/hover treatment that .shop-filter
    already had (was an oversight in the previous fix)
  • Active pills gain a darker shadow halo so they don't look detached
  • Progress tracks switch to a 10% white track instead of 7% black

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 21:31:53 +03:00
Maxim Dolgolyov 23075dddb1 fix(bg): light text on dark-tone backgrounds
The dark veil was right (deep navy at 78%), but every page chrome
element below it inherited light-theme text colors and faded to
invisible — 'Магазин наград' header, shop filter buttons, achievement
group titles, balance counter etc.

Targeted overrides for body[data-bg-tone='dark']: only the elements
that sit directly on the veil get a light text color. White cards
(.shop-item, .ach-item, .ep-card) keep their dark text intact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 21:29:54 +03:00
Maxim Dolgolyov d2ca0d61cc fix(bg): add translucent veil so animated bgs don't bleed UI
The single bg-fx layer was painting at full vibrancy behind the entire
app. Most UI elements use rgba() fills — chips, sub-panels, the
achievements .ach-item, the goal-tier bar — so saturated colors bled
right through, hurting readability on the Достижения / dashboard /
mocks tabs.

Layered fix:
  • bg-fx drops to z-index:-2 (the animated layer)
  • new #ls-bg-veil sits on z-index:-1 with rgba(245,247,251,.78)
    (light) or rgba(15,23,42,.55) when body[data-bg-tone='dark']
  • applyCosmetics injects both elements and tags the body with
    bg-tone based on the slug (dark/stars/aurora/nebula/grid go dark,
    everything else light)
  • clearing the bg removes both layers + the tone attribute

Result: animations stay perceptible (~22% of the chosen palette comes
through the veil), but the page chrome reads at normal contrast.

Shop swatches keep full vibrancy — the .bg-preview is meant to show
the raw palette so users can compare.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 21:27:35 +03:00
Maxim Dolgolyov 98ec1ed478 feat(shop): animated backgrounds — system-wide cosmetic + picker
A new cosmetic family: a fixed-position overlay painted behind every
page of the app, switchable from the profile shop. 4 free presets + 6
paid (250-1200 coins) so the new economy has another sink. Every
animation respects prefers-reduced-motion and falls back to its static
gradient.

Catalogue (migration 035):
  free:   none, gradient-soft, dots, dark
  paid:   gradient-flow, grid, bubbles, stars (mid)
          aurora, nebula                       (premium)

Backend:
  • migration 035 adds users.active_background + rebuilds shop_items
    CHECK to include 'background' (standard SQLite 'new + copy + swap')
    and seeds 10 items
  • shopController.getMyActive returns { background: { slug } } and
    activateItem handles type='background' (stores bare slug in
    active_background) + skips the user_purchases check for price=0
    so free presets work for everyone without per-user rows
  • routes/shop validate schema lets 'background' through

Frontend:
  • api.js applyCosmetics injects <div id='ls-bg-fx'> at body start
    and toggles class to bg-<slug>. Cleared backgrounds remove the
    element so dark→light transitions don't leave artifacts.
  • ls.css gains a self-contained 'ANIMATED BACKGROUNDS' block:
    keyframes per animated slug (ls-bg-flow, ls-bg-grid-scan,
    ls-bg-bubble-rise, ls-bg-stars-twinkle, ls-bg-aurora-spin,
    ls-bg-nebula-pan) wrapped in a prefers-reduced-motion kill-switch.
    Same .bg-<slug> classes are reused for the .bg-preview swatches.
  • profile.html shop:
    - new 'Фоны' filter button between Рамки and Титулы
    - _renderItemPreview type='background' draws a real 56-aspect swatch
      (same CSS as the page bg — what you see is what you apply)
    - _isItemActive matches by slug for background type
    - free items (price===0) treated as auto-owned in render so users
      can apply them without a fake 'purchase' step

Verified: getMyActive returns { background: { slug: 'nebula' } } after
flipping users.active_background; activate path updates the row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 21:13:53 +03:00
Maxim Dolgolyov 660e7e2747 feat(gamification): Phase 1 — full kill-switch + textbook XP wrapping
Until now the 'gamification' feature flag did nothing: it had no row in
app_settings, the admin couldn't toggle it, awardXP/awardCoins ignored
it, and the CSS only hid three dashboard widgets — XP bars in textbooks
stayed visible regardless.

Phase 1 closes every hole.

Backend (source of truth):
  • migration 029 seeds feature_gamification_enabled=1
  • new isGamificationEnabled() helper in gamification/_shared.js with a
    30s cache + invalidateGamificationCache() for instant admin toggles
  • awardXP / awardCoins / updateStreak / unlockAchievement /
    checkAchievements all bail out when the flag is off
  • /api/gamification/* and /api/shop/* (user routes) return 404 when
    disabled; admin routes remain open so the switch itself is reachable
  • adminController.updateFeatures gains 'gamification' in the allow-list
    and invalidates the cache on flip

Frontend:
  • LS.isGamificationEnabled() (synchronous, populated by loadFeatures)
    so xp.js + applyCosmetics can bail without a round-trip
  • xp.js load/add/flush become no-ops when the flag is off
  • applyCosmetics skips the round-trip when off
  • CSS .no-gamification rule expanded to cover .hero-xp-badge, .po-xp,
    .xp-card, .xp-bar, #frames-section, and a universal [data-gamified]
    hook for future blocks

Textbooks (Variant 2 of the plan):
  • backend/scripts/wrap_textbook_xp.py — idempotent script that adds
    data-gamified to 167 XP tags across 63 textbook files (chapters +
    hubs, all subjects/grades). Single CSS rule now hides everything.

Verified end-to-end: with the flag off, awardXP/awardCoins write nothing;
flipping back restores normal behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 19:43:24 +03:00
Maxim Dolgolyov 46d373752c fix(profile): visual frame previews in shop + sidebar avatar sync
Shop items of type 'frame' now render a real avatar-sized preview with
the frame's CSS applied (instead of a generic lucide icon) so buyers
see exactly what they're paying for. Title items get a tag-shaped
preview in their color. The avatar-frames section above the shop also
shows the user's actual avatar inside the frame circles, not 'LS' text.

Sidebar nav-avatar now:
  • renders the uploaded avatar_url instead of always showing initials
    (LS.initPage + new LS.refreshNavAvatar helper)
  • picks up frame CSS on every page via applyCosmetics — previously
    only dashboard.html applied it
  • repaints immediately after picking/deleting an avatar preset
    (avPickPreset / avDelete now call LS.setUser + LS.refreshNavAvatar)

Backend getMyActive resolves avatar_frame to {id, css} for both
gamification frames ('fire', 'crown', ...) and shop-purchased frames
('shop_<id>'), so the client doesn't need a second round-trip to
look up the CSS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 15:04:27 +03:00
Maxim Dolgolyov dfaa346051 feat(ls): expand design-system tokens + utility classes
Adds to ls.css:
- Spacing scale (--space-1..16, 4px base)
- Radius ladder (--r-xs/sm/md/xl in addition to existing lg/pill)
- Semantic color aliases (--success/warning/danger/info -> existing brand)
- Typography scale (--text-xs..3xl) + font-weight scale (--fw-*)
- Motion tokens (--ease-out, --ease-spring, --duration-*)
- Breakpoint documentation vars
- Utility classes (.hidden, .p-*, .m-*, .gap-*, .flex, .grid, .text-*, .rounded-*)
- Shared .ls-skeleton with shimmer animation

All existing tokens preserved unchanged -- additions only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 09:40:15 +03:00
Maxim Dolgolyov 26ba289019 a11y: WCAG AA contrast + ARIA roles + focus management across all pages
- css/ls.css: --text-3 #8898AA → #56687A (5.1:1 contrast), min-height 44px on .btn-primary/.btn-ghost/.sb-link, new .icon-btn utility (44×44px)
- js/api.js: lsConfirm — role=dialog, aria-modal, aria-labelledby, Tab focus trap, restore focus on close; lsToast — aria-live=polite on container, role=alert on errors; live quiz — role=dialog, role=radiogroup, role=radio, aria-checked, keyboard support
- test-run.html: q-opt divs — role=radio/checkbox, aria-checked, tabindex, keyboard enter/space; confirm modal — role=dialog, aria-modal; btn-flag — aria-pressed; dots — aria-label, aria-current; touch targets 44px
- board.html: btn-del-ann — aria-label; reaction buttons — aria-label, aria-pressed
- All 18 HTML files: replace hardcoded color:#8898AA with color:var(--text-3)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:42:38 +03:00
Maxim Dolgolyov 6429e07606 fix: sidebar profile always visible — only .sb-nav scrolls, .sb-foot pinned at bottom 2026-04-14 20:40:45 +03:00
Maxim Dolgolyov edb4c211a0 feat: universal sidebar via sidebar.js + stale ID cleanup
- Add js/sidebar.js: generates full sidebar HTML into #app-sidebar,
  handles role-based visibility, active link (with prefix matching),
  toggle wiring, collapsed state, board/features/notif init
- Replace <aside class="sidebar">...</aside> with <aside id="app-sidebar">
  across all 35 standard-layout pages via scripts/apply-sidebar.js
- Add notifications.js to 5 pages that were missing it
- Fix api.js initPage(): skip toggle re-wiring if data-sb-wired set,
  fix active link selector .sb-item → .sb-link
- Remove stale sbl-*/nav-admin/btn-upload-nav getElementById calls
  that crashed after sidebar replacement (lab, classes, collection,
  crossword, hangman, knowledge-map, library, pet, profile)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 21:22:21 +03:00
Maxim Dolgolyov be4d43105e LearnSpace: full-stack educational whiteboard platform
Node.js/Express backend + vanilla JS frontend.
Features: real-time collaborative whiteboard (SSE), multi-page support,
LaTeX formulas, shapes/connectors, coordinate systems, number lines,
compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with
rotation & resize controls, minimap navigation overlay, auto-measurements,
multi-page thumbnails sidebar, PNG export, page templates.
Student/teacher workflows: classes, assignments, library, dashboard.
Mobile responsive. SQLite (better-sqlite3).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 10:10:37 +03:00