Commit Graph

8 Commits

Author SHA1 Message Date
Maxim Dolgolyov 8091b48e1c fix(ct-math): практика возвращала меньше count + перенос заголовков в навигации урока
1) exam-prep practice (strategy=random) возвращал около 0.6 от count: функция
   distributeByDifficulty раскладывает count по 5 уровням сложности, а у трека
   ctmath задания только уровней 1-3 (уровни 4-5 пустые) -> часть выборки терялась
   (20 -> 12, 15 -> 10, 10 -> 6). В pickRandomByDifficulty добавлен добор до count
   из доступных уровней. Трек math9 не затронут (там добор не требуется).
2) lesson.html: .lesson-nav-btn-title был inline-span, поэтому max-width и ellipsis
   игнорировались и длинные заголовки вылезали за кнопку. Добавлен display:block.

Бэкенд-правка требует перезапуска сервера; фронт-правка видна после Ctrl+F5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:09:50 +03:00
Maxim Dolgolyov 95fee1d8c5 fix(security): убрать stored-XSS в блоке columns урока (Спринт1 #4)
Блок columns хранит rich-HTML из мини-редактора и рендерился сырым в innerHTML
(единственный неэкранированный блок) — учитель мог внедрить <img onerror>/script,
исполняемый у каждого ученика (кража JWT из localStorage). Добавлен санитайзер
sanitizeRichHtml (инертный template + вырезание on*/script/iframe/javascript:),
сохраняет форматирование, но блокирует исполнение.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 21:56:46 +03:00
Maxim Dolgolyov ef59023546 feat(lessons): SVG-рисовалка как блок урока (svg-draw)
Лёгкий векторный редактор frontend/js/svg-draw.js (перо со сглаживанием, линия,
прямоугольник, эллипс, стрелка, текст, цвет/толщина/заливка, выбор/перемещение/удаление,
undo/redo, очистка) → выдаёт чистый <svg>. Хранится inline в данных блока, переоткрывается
для дорисовки.

- Новый тип блока svg-draw: палитра «Рисунок», редактор (монтирование виджета + подпись),
  превью и студенческий рендер (lesson.html) — санитизированный inline-SVG, адаптивный.
- Санитайзер frontend/js/svg-sanitize.js (UMD, общий клиент/сервер): whitelist тегов/атрибутов,
  вырезает script/foreignObject/style/image/a, on*=, href, javascript:. Без зависимостей.
- Сервер (lessonController): svg-draw в VALID_TYPES + очистка data.svg при сохранении.
- Переиспользуемо: тот же виджет пригоден для флешкарт и фигур генератора задач.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 20:11:04 +03:00
Maxim Dolgolyov 5381679c68 chore: консолидация незакоммиченной работы (биохимия + System Health + lab/textbooks)
Зафиксирована накопленная незакоммиченная работа рабочего дерева, КРОМЕ файлов
учебника «Химия 7» (migration 046, chemistry_7_*.html, chem7_svg.js, тест —
оставлены незакоммиченными по запросу).

Включает: модуль биохимии (ядро BIO, 3D VSEPR, химдвижок, баланс, challenges,
пути из БД), System Health Level 1 (вердикт/мониторинг), а также frontend-
страницы и lab/textbooks-правки параллельной сессии.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 18:12:55 +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 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