Commit Graph

36 Commits

Author SHA1 Message Date
Maxim Dolgolyov c6d323ec6d feat(tests): витрина доступных тестов ученику + флаг «доступен ученикам»
Раньше ученик видел лишь 1 тест на предмет (дефолтный). Теперь учитель/админ
может пометить любой свой тест доступным, и он появляется в каталоге на дашборде.

- Миграция 079: tests.available_to_students (default 0).
- testController: list для ученика отдаёт тесты с available_to_students=1 и вопросами;
  create/update принимают флаг; update сделан частичным (не затирает поля при toggle).
- admin «Тесты»: бейдж «Доступен ученикам» + быстрый тумблер «Ученикам/Скрыть»
  (toggleTstAvail; конструктор доступен и учителям — видят свои тесты).
- Дашборд: виджет «Тесты» → секция «Доступные тесты» (loadAvailableTests), клик
  запускает фикс-тест. Прячется, если доступных нет.

⚠️ Живой БД нужен npm run migrate (колонка).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 11:03:42 +03:00
Maxim Dolgolyov c5d440a7a9 fix(tests): режимы доступных тестов только exam/practice + скрытие пустых предметов
Рассогласование: админ-настройка допускала режимы topic/random, но POST /api/sessions
принимает только exam/practice → клик по такому предмету падал с 400. Убрал topic/random
из валидатора subjects.js и из админ-дропдауна (SC_MODES). Дашборд: старые значения
topic/random коэрсятся в practice; предметы без вопросов в банке И без фикс-теста больше
не показываются (раньше давали 404 «No questions found» при запуске).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 10:53:43 +03:00
Maxim Dolgolyov 1aa95a6776 fix(dashboard): hero «Лаборатория дня» виден при выключенной лабе
Hero-карточка #hc-lab имела href="/lab", но loadLabOfDay меняет его на
/lab?sim=<id> → CSS [href="/lab"] больше не матчит, карточка оставалась видной.
Прячем по стабильному id: #hc-lab/#hc-pet/#hc-read добавлены в FEATURE_WIDGETS
(lab/pet/textbooks). .hero-row переведён на grid auto-fit (minmax 240) — сетка сама
подстраивается под видимые карточки без дыры; syncHeroRow прячет весь ряд, если
карточек не осталось (мобайл-медиазапрос не трогаем — без инлайн-колонок).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 10:37:41 +03:00
Maxim Dolgolyov 399a222b65 fix(dashboard): пустой бокс колонки прогресса, когда флешкарты отключены
#w-flashcard прятался, но он — секция внутри #w-progress-col (один .widget-бокс с
рамкой/паддингом: карточка + прогресс по предметам + результаты). Если все секции
скрыты (флешкарты выкл и нет данных), оставался пустой бокс. Добавлена
syncProgressCol(): прячет #w-progress-col, если ни одна секция не видна (computed-
display, учитывает и инъект-CSS флешкарт). Зовётся в конце loadFlashcardWidget /
loadLastResultsWidget / loadSubjProgressWidget.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 00:26:33 +03:00
Maxim Dolgolyov aee8597499 style(dashboard): визуальная полировка блока «Активность»
- hero-строка: крупное число занятий + тренд-пилюля со стрелкой
- сегментированный контрол масштаба (6н/12н/6м)
- ячейки тепловой карты: скруглённые квадраты, интенсивность через alpha, glow при наведении
- легенда типов — чипы-пилюли
- календарь «Месяц»: оттенок активных дней по нагрузке, пилюля стрика, мягкий ring сегодня
- паритет тёмной темы

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:22:01 +03:00
Maxim Dolgolyov 8e8f54b41b feat(dashboard): блок активности — все виды учёбы, тренд, разбивка по типам, empty-state
- Бэкенд /api/dashboard/activity: per-day агрегация активности по типам (тест/экзамен/карты/уроки/
  онлайн/домашка) из 6 таблиц за ~182 дня (раньше карта считала только тесты).
- Карта раскрашивается по доминирующему типу активности + легенда типов; интенсивность/размер по числу.
- Недельный тренд в футере («эта неделя N · +K к прошлой»).
- Тултип и попап по клику показывают разбивку дня по типам.
- Empty-state для новичков (вместо пустой сетки — призыв + CTA). Календарь «Месяц» тоже от всех активностей.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:06:46 +03:00
Maxim Dolgolyov da5e95bdaf feat(flashcards): картинки в дашбордном виджете «Повтори карточку»
renderFlashcardWidget рисует front_image/back_image на обеих сторонах;
.fcw-inner.has-img расширяет высоту карточки под изображение.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 13:09:59 +03:00
Maxim Dolgolyov 2d83896a9a fix(dashboard): hero-аватар показывает загруженную картинку, а не только инициалы 2026-06-01 10:00:46 +03:00
Maxim Dolgolyov 7d478c1c1b style(dashboard): редизайн sticky-шапки
- Аватар: 48px + violet glow shadow
- Шапка: высота 68px, blur 16px, border rgba(violet .1), box-shadow
- Статы: пилл-обёртка с фиолетовым тинтом и бордером; разделители между кольцами
- Stat rings: горизонтальный layout SVG(36px) + sr-val + sr-label; значение вынесено из SVG наружу — читается крупно цветным шрифтом

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:51:39 +03:00
Maxim Dolgolyov 57ffbc8ae6 style(dashboard): улучшен визуал гамификационной полосы
- Фон: radial-gradient пятно за бейджем уровня + inset highlight
- Бейдж уровня: 56px, гло-тень + внешнее кольцо rgba
- Progress bar: 10px, 3-стоп градиент + box-shadow свечение
- Разделитель border-left между прогрессом и чипами
- Чипы: белый фон с тенью, увеличен padding, крупнее шрифт

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:47:28 +03:00
Maxim Dolgolyov b22a1fad3c style(dashboard): улучшен визуал карточки питомца
- Тёплый кремовый градиент фона вместо чистого белого
- Декоративный SVG-фон: рассыпанные искры/звёздочки
- Тег-пилл с янтарным оттенком
- Glow-эффект drop-shadow вокруг спрайта питомца
- Progress bar 7px с оранжевым свечением
- Цветные чипы: стрик → огненный, цель → изумрудный, настроение → фиолетовый
- Кнопка «Ухаживать» — градиент жёлтый→оранжевый с тенью

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:40:50 +03:00
Maxim Dolgolyov ed8323cbb9 style(dashboard): улучшен визуал карточки «Продолжить чтение»
- Декоративный SVG-фон (открытая книга) поверх градиента
- Тег-пилл с frosted-glass эффектом
- Progress bar 7px с белым свечением
- Мета и процент сгруппированы слева, кнопка — справа с тенью
- Градиент обогащён третьим стопом (#8b3010)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:33:50 +03:00
Maxim Dolgolyov 927b39b0d6 feat(dashboard): «Лаборатория дня» синхронизирована с каталогом /api/lab/sims
Раньше карточка использовала захардкоженный список из 6 симуляций и не знала
о каталоге. Теперь ежедневный выбор берётся из /api/lab/sims: только включённые
симуляции, у которых есть превью (приоритет featured), title/категория — из БД,
поэтому переименование/выключение/рекомендация в админке отражаются автоматически.
Время/уровень/цель — из curated-карты по id (в каталоге их нет) c дефолтами.
Фолбэк на статичный список, если API недоступен. Заодно исправлен mismatch
isoprocess→molphys (href теперь = id симуляции).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:20:10 +03:00
Maxim Dolgolyov 06b23c36dd fix(dashboard): пустой виджет карточек — кнопка ведёт на /flashcards
Кнопка «Создать карточку» в пустом состоянии вызывала click() по FAB,
но исходный клик всплывал до document-листенера, который сразу закрывал
поп-ап — внешне ничего не происходило. Заменено на ссылку на /flashcards.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:10:34 +03:00
Maxim Dolgolyov 0d2ddee874 feat(dashboard): карточка чтения берёт данные и цвет из «Учебников»
Источник — /api/textbooks (как страница «Учебники»):
- учебник в процессе (есть прочитанные §) → «Продолжить чтение» с
  прогресс-баром и «N из M § прочитано», ссылка на last_para;
- иначе первый учебник каталога → «Начать чтение», «M § · новый учебник»;
- фон карточки = градиент обложки по t.color (TB_COVER — зеркало
  .tb-cover из textbooks.html), полная синхронизация цвета.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 12:32:00 +03:00
Maxim Dolgolyov 6551990e8e fix(dashboard): иконка испытания «Марафонец» (running → footprints)
Иконка Lucide 'running' не существует, поэтому createIcons() оставлял
<i> пустым — у испытания типа 'tests' не было иконки. Заменено на
валидную 'footprints'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 12:20:33 +03:00
Maxim Dolgolyov 4fed35fec8 feat(dashboard): карточка чтения наполняется данными (фон+инфо учебника)
Раньше при отсутствии начатого курса карточка оставалась статичной
заглушкой («Учебники»). Теперь:
- при прогрессе — «Продолжить чтение»: курс, урок, прогресс-бар, %;
- иначе — рекомендованный учебник из /api/courses: название, описание,
  число параграфов;
- фон-градиент карточки по предмету (SUBJ_GRADIENT, как обложки).
Синтаксис всех инлайн-скриптов проверен (0 ошибок).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 12:18:40 +03:00
Maxim Dolgolyov 2addb8ec02 fix(dashboard): Активность видна всегда + ряд одинаковой высоты
- loadActivityWidget показывает блок всегда (пустое состояние рисует
  renderHeatmap), даже при 0 сессий и при ошибке истории.
- .bottom-grid: align-items stretch + height 100% — карточки ряда
  (Активность/Мои сдачи/Испытания) одной высоты.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 12:02:32 +03:00
Maxim Dolgolyov edfa799d9c feat(dashboard): «Активность» в нижний ряд + удалён остаток «Теории»
- Блок «Активность» (heatmap + календарь) вынесен из 3-й колонки в
  отдельный нижний ряд .bottom-grid рядом с «Мои сдачи» и «Испытания».
- Удалён остаток разметки «Теория — в процессе» и разметка рейтинга
  (lb-section) с дашборда; конфиг виджетов обновлён (Активность вместо
  Теории/Рейтинга).
- Селектор скрытия для учителя и адаптив обновлены под .bottom-grid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 11:39:54 +03:00
Maxim Dolgolyov 5a93751ccc fix(dashboard): синхрон XP питомца 1-в-1 с модулем /pet
Полный XP и абсолютный порог уровня (d.xp / d.xpForNextLevel),
уровень пользователя d.level — как в pet.html, а не относительный
расчёт по petLevel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 11:31:08 +03:00
Maxim Dolgolyov 5c611166f3 fix(dashboard): добавлено определение loadPetHero (ReferenceError на проде)
Функция loadPetHero вызывалась, но её тело не попало в коммит
667054f (Edit не применился). Восстановлено: рендер питомца через
PetSprite + загрузка /api/pet, как и задумано.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 11:18:46 +03:00
Maxim Dolgolyov c6662b3056 refactor(dashboard): убран блок «Теория» и «Рейтинг» с главной
- Рейтинг (lb-section) перенесён в профиль — удалён с дашборда вместе
  с вызовами loadLeaderboard()/_populateLbClasses() и тоглом конфига.
- Виджет «Теория» (w-theory-progress) удалён вместе с тоглом конфига.
- applyDashboardPrefs/toggleDashWidget null-безопасны к удалённым id.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 11:14:24 +03:00
Maxim Dolgolyov 667054fa58 feat(dashboard): hero-карточки главной — чтение, лаборатория дня, питомец
Пересборка верхней зоны дашборда по скриншоту (редизайн был утерян):
- 3 hero-карточки вместо action-cards: «Начать чтение» (продолжение
  курса через /api/courses/continue), «Лаборатория дня» (детерминир.
  выбор по дню + SVG-превью из lab-previews.js), «Питомец» (синхрон
  с модулем /pet через /api/pet + единый PetSprite.render).
- Подключены восстановленные ассеты pet-sprite.js и lab-previews.js.
- Убран weak-topics из hero; питомец показывает уровень/XP/стрик/
  цель дня/настроение, синхронно со страницей /pet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 11:11:20 +03:00
Maxim Dolgolyov 8629616a04 feat(dashboard): командный центр администратора на /dashboard
Админ при входе на /dashboard видит редизайн-обзор (порт макета
admin-dashboard-redesign.html) на реальных данных /api/admin/overview:
KPI-пульс со спарклайнами, инбокс «Требует внимания» с табами
(блокировки/зависшие/брошенные), лента топ-сессий, распределение по
предметам, здоровье контента, топ/худшие результаты, быстрые действия.
Стили заскоуплены под #admin-command-center. Учитель/ученик без изменений.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 10:58:43 +03:00
Maxim Dolgolyov 1dcc4cbf6e feat(flashcards): глобальный quick-add FAB + виджет «повтори карточку»
Backend:
- POST /api/flashcards/quick — добавить карточку из любой точки; колода по
  выбору или автоколода «Быстрые карточки» (создаётся при первом обращении)
- GET /api/flashcards/random — случайная карточка из всего пула пользователя

Frontend:
- /js/flashcard-fab.js — плавающая кнопка «запомнить» на всех страницах
  (учебник, лаборатория, симуляция…). Поповер: вопрос/ответ/колода, Ctrl+Enter.
  Гейт по фиче-флагу flashcards; исключены classroom/login/error/сама /flashcards.
  Загружается лениво из sidebar.js (на 45 страницах с шапкой).
- dashboard: виджет #w-flashcard в колонке прогресса — флип-карта (вопрос↔ответ),
  кнопка «Другая», счётчик пула, CTA при пустом пуле; слушает событие
  flashcard:added для авто-обновления.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 09:38:23 +03:00
Maxim Dolgolyov 58cff2285e refactor(dashboard): use ls.css design tokens
Replaced 42 hardcoded brand colors with var() tokens:

CSS rules:
- linear-gradient(135deg,#9B5DE5,#06D6E0) → var(--grad-1) in dh-avatar, lb-avatar, gam-level
- #0F172A → var(--text) in 28 CSS rules (text colors for all major widget labels,
  titles, buttons: dh-greeting, ac-title, qa-btn, grade-subj, act-tab.active,
  hm-footer strong, hm-tip bg, hdp-date/subj, assign-search, sc-month,
  qs-subj-btn, qs-select/input, adm-act, adm-sess-name, cs-name, assign-chip.active,
  ae-btn, ar-title, ar-btn, hist-subj, subj-chart-name, weak-name,
  ms-title, empty-cta-title, btn-join, stats-bar tooltip bg, stats-chip-val,
  tc-title, lb-title, lb-name, lb-class-sel, ch-title, gam-rank, gam-chip-val)
- #3D4F6B → var(--text-2) in hist-score, ae-submit-note
- #9B5DE5 → var(--violet) in lb-xp, ar-sub-chip.s-none, lb-tab.active,
  ch-pct, ch-xp, dash-cfg-title, dash-cfg-fab, dash-cfg-checkbox accent-color
- #06D6E0 / #F15BB5 → var(--cyan) / var(--pink) in stat rings and progress fills

JS-generated HTML/SVG:
- var(--violet)/var(--cyan) in SVG statRingSvg calls, goal ring SVG, barColor
- weak-fill gradient partial replacement (--pink)

Kept: 90deg progress bar gradients, domain palette colors (#E0335E/#059652/#7c3aed
/#05aab3/etc.), Chart.js hex configs (CSS vars unsupported there), canvas fillStyle,
action banner dark gradients, heatmap cell alphas, adm-act-icon domain navcolors.

Before: ~65 hardcodes | After: ~20 hardcodes (Chart.js, canvas, domain/dark theme)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 22:19:02 +03:00
Maxim Dolgolyov d3b1cd75a0 feat(dashboard): teacher view polish — chips, bars, KPIs, groups, mobile
P0 visual polish:
- adm-actions: grouped layout (teaching/content/admin) with 3-col grid at wide, responsive
- thick 8px colored progress bars (green ≥75 / amber 50-74 / pink <50)
- session % rendered as colored chip (tinted bg + border)
- hover state on .adm-sess-row and .asgn-row in admin-grid
- empty states with Lucide icon + CTA button (inbox/users/clock)
- class-name badge on assignment row (disambiguates duplicates)
- relative timestamp on session rows via relativeAgo()
- search input above assignment list (filterAdminAssignments())
- adm-act-icon bumped 16px → 20px; card hover: scale + shadow

P1 header KPIs + urgency:
- dh-kpi-row: classes / students / active-asgn / pending chips under greeting
- isTeacherUrgent(): assignments within 48h get pink border + срочно badge
- adm-act-badge: count badge on Мои классы and Работы cards
- loadTeacherKPIs() fetches /api/classes + teacherAssignments() in parallel

P2 grouping + mobile + micro:
- chevron-right icons on Все/Все классы section links
- mobile ≤640: single-column groups, KPI chips wrap, sess-rows wrap
- mobile ≤480: adm-act-group single column
- dark mode rules for new elements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 15:25:34 +03:00
Maxim Dolgolyov 5c66105fc2 refactor: ещё 6 модалок → LS.modal (dashboard, theory, course)
dashboard.html: 2 → 0 
  - join-modal — вступить в класс
  - qs-modal — быстрый тест с выбором предмета + режим + кол-во

theory.html: 1 → 0 
  - new-course-modal — создание нового курса учителем

course.html: 4 → 0 
  - add-section-modal — новый раздел курса
  - edit-course-modal — редактирование курса
  - add-lesson-modal — новый урок
  - save-course-tpl-modal — сохранить курс как шаблон

Везде:
  - Inline <div class=\"modal-overlay\">...</div> → удалён
  - openX(): создаёт modal через LS.modal({content, actions})
  - closeX() удалена — _xModal.close()
  - Глобальный selectQsSubject() inline'нут как listener на body модалки
  - Enter-handler на главных inputs сохранён

Не трогаю:
  - biochem.html#lib-modal — кастомная тёмная тема, не подходит под
    светлый LS.modal без редизайна
  - library.html — 3 сложные модалки (folder-access, assign, upload)
    с tabs и dynamic state — отдельный заход
  - classes.html — modal-assign (128 строк, complex) + review-modal
  - flashcards.html — fc-modal (не modal-overlay, своя CSS)

Прогресс миграции: 12 простых модалок → LS.modal за серию (4 ранее
+ 2 ранее + 6 сейчас). 4 страницы полностью очищены от
modal-overlay. Унифицированы:
  - ESC/backdrop/focus поведение
  - z-index (9000)
  - Анимация (scale .22s)
  - Адаптив на мобилке
2026-05-16 19:33:39 +03:00
Maxim Dolgolyov 3ff2f01178 feat: textbooks Phase 4 — A1+A2+A3+B4+C7 + назначение ученику
A1 — карточка ДЗ-чтения у ученика на /dashboard:
  - Новая ветка в buildAssignCard для assignments с textbook_id
  - Прогресс-бар «X из Y §», цвет берётся из textbook.color
  - Кнопка «Открыть / Продолжить» с deep-link на первый требуемый параграф
  - В classify(): textbook_all_read → done, deadline → overdue

A2 — авто-проверка выполнения:
  - При POST /:slug/progress с mark_read: проверяются активные textbook-assignments
  - Если все требуемые § прочитаны → INSERT в assignment_completion
  - SSE-уведомление учителю «Ученик завершил чтение: <title>»
  - myAssignments возвращает completed_at и textbook_all_read

A3 — учительский UI прогресса класса:
  - Новая страница /textbook-progress (учитель/админ)
  - Селекторы «учебник × класс» → таблица учеников с прогрессом
  - Сортировка по количеству прочитанного, дата last_at
  - Кнопка «Прогресс класса» добавлена в /textbooks (видна учителям)

B4 — admin-UI управления учебниками:
  - /admin-textbooks (только admin) — таблица всех учебников
  - Inline-редактирование title/author, тоггл is_active
  - Колонка «Читателей» (count из textbook_progress)
  - Endpoints: GET /api/textbooks/admin/all, PATCH /admin/:id

C7 — закладки/заметки внутри учебника:
  - Таблица textbook_bookmarks (user, textbook, para, text, note, color)
  - API: GET/POST/PATCH/DELETE для CRUD закладок
  - В tracker: при выделении текста (8-400 симв) появляется плавающая «+ Закладка»
  - Кнопка-иконка в overlay top-left открывает панель «Мои закладки»
  - Хранится paragraph-якорь, цвет, заметка, кнопка удалить

Назначение ученику (в дополнение к классу):
  - В модалке /textbooks — переключатель «Классу / Ученику»
  - Поиск ученика по имени/email через /api/classes/students
  - Submit использует POST /api/assignments (createDirectAssignment)
  - createDirectAssignment расширен textbook_slug + textbook_paragraphs
  - Учитель может назначать только ученикам своих классов

myAssignments расширен: возвращает textbook fields + post-process
  считает textbook_required_count, textbook_read_count, textbook_all_read.

Deep-link поддержка: /textbook/<slug>#pN в tracker.js — на load и hashchange
вызывает setParaTab(pN) (нативная функция учебника).

Миграция 005: assignment_completion + textbook_bookmarks + индексы.
2026-05-16 16:37:11 +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 f3c9ab860e feat: sound system — 12 new sounds + navigation category; dashboard FAB widget button
Sounds:
- UI: modal_open, modal_close, tab_switch, delete
- Navigation (new category): page_enter, section_reveal
- Classroom: timer_warning, wb_clear, file_shared
- Gamification: challenge_complete, daily_login
- Quiz: time_up, quiz_bonus

Dashboard:
- Widget configurator moved from header to fixed FAB (bottom-right)
  no longer pushed off-screen by wide sidebar

Profile settings:
- Added Navigation category toggle
- Expanded preview section: 12 test buttons covering all categories

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 20:26:04 +03:00
Maxim Dolgolyov 89ba25cd20 feat: user preferences sync — server-side storage, whiteboard defaults, dashboard widget visibility
- New table `user_preferences` (user_id PK, JSON blob, updated_at)
- GET/PATCH/DELETE /api/preferences with deep-merge UPSERT
- LS.prefs singleton in api.js: dot-notation get/set, debounced flush (1.5s), server sync
- classroom.html: load wb.color/width/lineStyle/theme from prefs on init; save on change
- dashboard.html: widget configurator panel (gear button) — toggle visibility per-user, persisted server-side

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 20:17:25 +03:00
Maxim Dolgolyov 29aa985504 feat: add sound system (LS.sfx) — synthesized Web Audio API sounds for classroom, gamification, quiz
- New js/sound.js: shared LS.sfx module with 21 synthesized sounds (ADSR envelope, sequences, sweeps, noise)
- Classroom: lesson_start/end, user_joined/left, hand_raise, chat_message, muted, draw_permitted
- Dashboard: achievement, level_up, xp_gain, coin via SSE events
- Live quiz: quiz_start, quiz_end on question launch and results
- Settings panel: global enable toggle + volume slider + localStorage persistence
- Replaces old _crBeep() in classroom.html

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:43:13 +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 fd29acbbdd feat: WebSocket real-time + rAF render gate + guest board + screen picker
Classroom performance:
- WebSocket server (ws-server.js) for low-latency cursor & stroke preview
  Replaces HTTP POST per event → eliminates per-message auth overhead
  Session member cache (30s TTL) avoids SQLite query per WS message
  Fallback to HTTP POST when WS not connected
- Cursor throttle reduced 100ms → 33ms (~30fps)
- Stroke preview throttle reduced 50ms → 20ms
- whiteboard.js: render() is now rAF-gated (_doRender/_rafPending)
  Multiple render() calls within one frame collapse into one repaint
  document.hidden check — zero CPU when tab is in background
  visibilitychange listener restores canvas on tab focus

Guest board:
- guestClassroom.js route: public token-based read-only access
- guest-board.html: name entry + read-only whiteboard + SSE
- SSE: addGuestClient/removeGuestClient/emitToGuests

Screen share picker:
- Discord-style modal with tab switching (screen/window/tab)
- Live video preview before confirming share
- useExistingScreenStream() in ClassroomRTC

Fullscreen exit overlay:
- #cr-fs-exit-overlay button inside cr-board-wrap
- Visible only via CSS :fullscreen selector (touchpad users)

File sharing from library:
- Teacher picks file from library, sends as styled card in chat
- crDownloadLibraryFile() fetches with Bearer auth

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:04:59 +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