Commit Graph

948 Commits

Author SHA1 Message Date
Maxim Dolgolyov 952a54f97c security+perf: полное ревью — 17 фиксов P0/P1 (XSS, IDOR, race conditions, rate limits, TURN, WAL)
## P0
- admin.html:2608, red-book-ecosystem.html:489-495 — XSS: u.name/node.name_ru/description обернуты в LS.esc()
- classController.js getAnnouncements — добавлена проверка teacher_id (B14: учитель A не может читать объявления класса B)

## P1 — auth & validation
- authController.js — минимум пароля 6→8 символов (register + change password + login.html)
- gamificationController adminAward — валидация max XP/coins (1M), Number coercion
- shopController adminAwardCoins — валидация max + проверка changes>0

## P1 — race conditions
- petController.buyBg — atomic UPDATE WHERE coins>=? (race-safe)
- shopController.purchaseItem — atomic conditional UPDATE
- liveController — добавлен question_id в live_answers (миграция с пересозданием таблицы), история ответов сохраняется при смене вопроса учителем
- ws-server: invalidateDrawCache экспортирован, classroomController grant/revoke вызывают его → permission revoke применяется мгновенно (раньше до 10s stale)

## P1 — rate limits & retry
- rateLimit middleware: новый параметр byUser=true (использует req.user.id вместо IP — не блокирует пользователей за NAT)
- routes/classroom.js: reactionLimiter (15/5s) на /chat/:msgId/react, handLimiter (5/5s) на raise/lower hand
- api.js sendAnswer — retry 3x с exp backoff (300/1200/2700ms), не повторяет на 4xx (F5)

## P1 — performance
- classroomController.getStrokes — LIMIT 5000 + флаг hasMore (защита от OOM на 10K+ strokes)
- whiteboard.js _liveStrokes — TTL 1.5s на каждый live preview (auto-cleanup при крашe ремоут юзера)

## Infrastructure
- config.js: TURN_URL/USER/PASS env vars
- server.js: GET /api/ice-servers возвращает STUN + опциональный TURN из env
- classroom-rtc.js: фетчит /api/ice-servers вместо хардкода (поддержка TURN для NAT/CGNAT школьных сетей)
- .env.example: документация TURN
- db.js: PRAGMA synchronous=NORMAL (5x быстрее с WAL), cache_size 16MB, temp_store=MEMORY
- ws-server.js closeAll() + server.js shutdown — graceful WS shutdown при SIGTERM

## False positives (не баги, агенты ошиблись)
- assignmentController FK на tests — на самом деле users (migrate.js:317-318)
- .env в git — gitignore корректно исключает
- admin.html без requireAuth — есть LS.initPage() который вызывает requireAuth
- submissionsController IDOR — обе ручки уже проверяют teacher_id
- screenSender = null inside try/catch — на самом деле снаружи
- SSE без backoff — есть exponential 2s→30s
- sessionController NOT IN на пустом массиве — есть guard usedIds.length>0
- getChat без LIMIT — есть LIMIT 100/200
- trust proxy — установлен на server.js:105

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 12:16:08 +03:00
Maxim Dolgolyov 2ae06ba2f1 refactor: B5 + B7 — pet DDL в migrate.js, requirePermission для admin-роутов
B5: убрать inline ALTER TABLE из petController.js → перенести в migrate.js
    (pet_name, pet_color, pet_last_petted, pet_petting_streak, pet_last_star,
     pet_bg, pet_bg_owned, pet_last_fed)

B7: gamification/shop admin endpoints:
    - /admin/award, /admin/stats, /admin/user/:id → requirePermission('gamification.manage')
    - /shop/admin/items(GET), /admin/award-coins, /admin/stats → requirePermission('shop.manage')
    - Деструктивные операции (reset, create/update/delete items) — requireRole('admin')
    Оба пермишна существуют в PERM_DEFAULTS (default false для teacher, admin всегда проходит)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:46:42 +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 3a4623a60a fix: полное ревью системы — 15 исправлений безопасности и надёжности
Безопасность:
- tests/🆔 скрыть is_correct и explanation для студентов (P0)
- SQL injection: limit/offset через placeholder вместо template literal
- Stored XSS: stripTags для lesson comments, flashcards, redBook sightings
- profile.html: escape e.message в showMsg (XSS через server error)
- attachment_url: валидация только /uploads/* путей
- requestId: генерировать UUID сервером, не доверять клиенту
- register: скрыть token_version из ответа

Надёжность:
- register: обработка UNIQUE constraint race condition
- pet buyBg: re-check баланса внутри транзакции
- DB errors: скрыть e.message в testController/questionController/courseController
- preferences: лимит 50KB на размер JSON

UX:
- board.html: debounce 250ms на search input

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 10:59:19 +03:00
Maxim Dolgolyov 6cd0cf34d4 fix: глубокое ревью онлайн-урока — 14 исправлений (P0-P3)
P0 — краши:
- CREATE TABLE classroom_hands в migrate.js (отсутствовала)
- emit→emitToUser для allowDraw/revokeDraw/mutePeer (WS доставка)
- deleteHistorySession обёрнут в db.transaction() + добавлена очистка hands/invites

P1 — гонки и безопасность:
- deletePage: 4 SQL в транзакции (race при параллельной записи)
- postStrokes: MAX(seq) внутрь транзакции (дубли seq)
- duplicatePage: добавлен seq в INSERT (NOT NULL crash)
- hasAccess для lowerHand/getHands/reactToMessage (утечка данных)
- loadTemplate: проверка owner шаблона
- attachment_url: только /uploads/* (XSS через javascript:/data: URI)
- wbFlushBatch: backoff при ошибке (было 12.5 req/s retry)
- pagehide leave: keepalive fetch для гарантированной доставки
- _wbOwnIds: cap 2000 (утечка памяти на длинных уроках)

P2-P3:
- simState: лимит 64KB (предотвращает OOM broadcast)
- ws-server кеши: cleanup drawCache при invalidateSession

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 09:22:39 +03:00
Maxim Dolgolyov f1e6ed7f2d fix: ревью онлайн-урока — 8 исправлений багов, уязвимостей и улучшений
- draw_permitted: emit→emitToUser (WS доставка вместо SSE-only)
- raised hands: убран in-memory Map, единый источник — таблица classroom_hands
- endSession: очистка classroom_hands при завершении сессии
- VALID_THEMES: исправлен список (добавлен corkboard, убраны dark/grid/dots)
- XSS: crLoadOnlineStudents — inline onclick заменён на data-* + addEventListener
- signal(): проверка что target_user_id является участником сессии
- WS rate-limit: 120 msg/sec per connection
- invalidateSession при join/leave для мгновенной видимости новых участников

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 09:02:50 +03:00
Maxim Dolgolyov 847e9b9b4f fix: добавить video.play() для демонстрации экрана — предотвращает блокировку autoplay браузером 2026-04-15 19:22:12 +03:00
Maxim Dolgolyov e18945863f fix: WebRTC сигналы теперь доставляются через WS если пользователь подключён через WebSocket
signal() в classroomController использовал emit() из sse.js напрямую —
сигналы не доходили до пользователей на WebSocket-соединении (учитель).
Исправлено: используем emitToUser() из ws-server.js, который роутит в WS
и падает на SSE только как fallback.
2026-04-15 13:53:10 +03:00
Maxim Dolgolyov 8a86ff3fe5 debug: add RTC diagnostic logs (ICE state, ontrack, sendOffer, signal flow) 2026-04-15 13:45:07 +03:00
Maxim Dolgolyov c3050b9e43 fix: WebRTC audio — glare handling (Perfect Negotiation) + autoplay + signalingState guard 2026-04-15 13:28:38 +03:00
Maxim Dolgolyov 7f23cfdacd fix: тема доски синхронизируется между учителем и учениками в реальном времени
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 21:18:44 +03:00
Maxim Dolgolyov 366ad6e13e feat: красивое модальное окно редактирования аватара с кропом и зумом
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 21:10:42 +03:00
Maxim Dolgolyov c2eb319162 feat: avatar moderation — ученик загружает фото, учитель/админ подтверждает или отклоняет
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 20:55:45 +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 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 ba20a76839 feat: add sound & prefs settings tab in profile
- New 'Настройки' tab in profile.html between Закладки and Безопасность
- Sound section: global enable toggle, volume slider, per-category toggles (ui/classroom/gamification/quiz)
- Test buttons to preview each sound category
- Appearance section: animation toggle, push notifications toggle
- All sound prefs persist via LS.sfx localStorage (ls_sfx key)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:47:59 +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 d2bf3aba47 fix: цвет текста кнопок-действий в панели стереометрии 2026-04-14 14:19:28 +03:00
Maxim Dolgolyov e4f7c167eb refactor: переработана панель стереометрии — иконки, toggle-ряды, stepper для n 2026-04-14 14:14:37 +03:00
Maxim Dolgolyov d822b705c4 feat: удаление последнего измерения и очистка всех измерений 2026-04-14 14:05:40 +03:00
Maxim Dolgolyov 74fe2c4179 fix: добавить дуги оснований конуса/цилиндра в _edges для постановки точек 2026-04-14 14:02:22 +03:00
Maxim Dolgolyov fff22f7331 feat: засечки рёбер, производные точки 3D, длины рёбер в стереометрии 2026-04-14 13:59:32 +03:00
Maxim Dolgolyov 481a9aeb02 feat: стереометрия — усечённая пирамида, правильные многогранники, скрещивающиеся прямые
- Добавлены фигуры: усечённая пирамида, октаэдр, икосаэдр, додекаэдр
- Формулы V, S, r_вп, R_оп для всех новых фигур
- Инструмент '∠ скрещ. прям.' — угол и расстояние между скрещивающимися прямыми (4 клика)
- Для икосаэдра/додекаэдра — THREE.IcosahedronGeometry/DodecahedronGeometry с извлечением рёбер
- Вписанная/описанная сфера поддерживает октаэдр, икосаэдр, додекаэдр
- Параметр n добавлен для пирамиды

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 11:59:42 +03:00
Maxim Dolgolyov b520f4b849 feat: режим аннотации поверх симуляции в онлайн-уроке + fix планиметрии (arcmark, triangle tools)
Онлайн-урок:
- Кнопка «Рисовать» в баре симуляции (только учителю)
- При активации: холст доски показывается поверх iframe (z-index), фон прозрачный
- Учитель рисует прямо поверх симуляции обычными инструментами
- Студенты видят то же самое через SSE (classroom_sim_annotate)
- Выход из режима → кнопка «Вернуться к симуляции»

Планиметрия (bugfix):
- arcmark теперь рисуется всегда (не зависит от showAngles)
- altitude/median: 1 клик на вершину треугольника (авто-находит противоположную сторону)
- centroid/orthocenter: 1 клик внутри/на треугольник

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 11:31:39 +03:00
Maxim Dolgolyov 84dac03e53 fix: баги штрихов/дуг планиметрии + переработка инструментов треугольника; фаза 10.1 (теорема Фалеса)
- arcmark: рисуется всегда (не только при showAngles=true)
- altitude/median: 1 клик на вершину треугольника (авто-определение)
- centroid/orthocenter: 1 клик внутри/на треугольник
- thales: 3 клика O, A, B → A'B' параллельно AB, коэф. k

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 11:24:43 +03:00
Maxim Dolgolyov bdb81ba9c8 feat: планиметрия фазы 8.3–10.2 — метки параллельности, средняя линия, параллелограмм, диагонали, подобие; geometry в онлайн-уроке
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 11:15:46 +03:00
Maxim Dolgolyov 2191e53529 feat: Фазы 7–8 планиметрии — элементы треугольника + метки
Фаза 7:
- altitude: высота (клик на сторону → клик вершина → foot + отрезок + прямой угол)
- median: медиана (3 клика A,B,C → midpoint + отрезок)
- centroid: 3 клика → 3 медианы + точка G (centroid constr)
- orthocenter: 3 клика → 3 высоты + точка H (orthocenter constr + altitude_foot constr)
- gOrthocenter() math function
- Прямые углы для altitude_foot в _drawAngleMeasures
- Исправлен баг onHintChange: передавался boolean вместо numeric phase

Фаза 8:
- tick tool: метки равных сторон на отрезках и сторонах полигонов (1–3 штриха)
- arcmark tool: метки равных углов на вершинах полигонов (1–3 дуги)
- _drawTickMark(), sideMarks[], angleMarks[] на полигонах
- Новая секция «Метки» в панели инструментов

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 11:08:16 +03:00
Maxim Dolgolyov 76428db513 feat: Фазы 6.1–6.3 планиметрии — высоты, прямые углы, удаление объектов
6.1 Стороны полигонов теперь выбираются как опорные линии (_hitTestLine),
    через виртуальные сегменты (virtual:true, polyId). Cascade-удаление
    исправлено на BFS (transitive deps). Теперь можно строить высоты треугольников.

6.2 Прямой угол (квадратный маркер) рисуется для всех foot-конструкций
    в _drawAngleMeasures, независимо от полигона.

6.3 Удаление отдельных объектов: onDeleteRequest callback, диалог
    «Только этот» (derived-точки → свободные) / «Со всеми зависимыми»
    (cascade) / «Отмена». CSS-панель .geo-del-confirm поверх canvas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 11:00:19 +03:00
Maxim Dolgolyov 0523734898 feat: Phase 5 планиметрии — касательные (tangent) + параллельный перенос (translate)
- gTangentPoints(O, P, r): касательные через полярно-полярную точку M=O+v*r²/d, h=r√(d²-r²)/d
- tangent: 2 derived_line (which=0/1) из внешней точки к окружности; оба пересчитываются
  при движении точки или изменении радиуса/центра; _pendingCircRef хранит окружность-источник
- translate: derived point P'=P+(B-A) по вектору AB; 3-фазный ввод с onHintChange(tool,2/3)
- _hitTestCircle(): найти окружность под курсором (HIT=12px)
- _drawLineRefHighlight(): расширен для circle (рисует дугу подсветки)
- _pendingCircRef очищается в setTool()
- lab.html: кнопки Симметрия/Перенос/Касательные, _GEO_PHASE_HINTS словарь,
  _geoShowHint(name, phase) принимает числовой phase вместо boolean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 10:28:09 +03:00
Maxim Dolgolyov e2e351d9c2 feat: Phase 4 планиметрии — симметрия (reflect) + правильный n-угольник (ngon)
- GeoEngine: _dependsOn/recompute для constr='reflect' и 'ngon_vertex'
- reflect: производная точка-отражение (P'=2·foot-P), зависит от axis+srcPt
- ngon: правильный n-угольник по центру и вершине; вершины v1..vn-1 = derived
  points (constr='ngon_vertex', хранят srcCenter/srcVertex/k/n); при движении
  центра/вершины все вершины автоматически пересчитываются
- GeoSim: _ngonSides=6, setNgonSides(n), инструменты 'reflect'/'ngon' в _handleToolClick
- lab.html: кнопки Симметрия и n-угольник, +/- контроллер сторон, хинты Phase 2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 10:22:49 +03:00
Maxim Dolgolyov 2e7ec81e59 feat: Phase 3 планиметрии — дуги углов, маркер 90°, инструменты foot/circumcircle/incircle
- _drawAngleMeasures(): реальные дуги через биссектрису (midAngle±halfSpread),
  маркер правого угла квадратом при |angle-90°|<2°
- gIncircle(): функция вписанной окружности треугольника
- GeoEngine: поддержка constr='foot' (точка), constr='circumcircle'/'incircle' (окружность)
  в _dependsOn и recompute; cascadeDelete через derived circles
- _drawCircle(): обработка derived=true (circumcircle/incircle) — dashed + cx/cy/r
- getStats(): исправлена статистика для производных окружностей
- constructions учитывает derivedCircles
- lab.html: 3 новые кнопки (Основание, Описанная, Вписанная) + хинты

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 10:15:05 +03:00
Maxim Dolgolyov 95cca89dfc feat: планиметрия Phase 2 — инструменты построения
- GeoEngine: система производных объектов (recompute, propagateDeps),
  каскадная цепочка зависимостей при перемещении точек
- 6 новых инструментов в GeoSim:
  * midpoint — середина отрезка (производная точка)
  * perpbisect — серединный перпендикуляр (derived_line)
  * anglebisect — биссектриса угла ABC (derived_line)
  * parallel — параллельная прямая через точку (derived_line)
  * perpendicular — перпендикуляр через точку (derived_line)
  * intersect — точка пересечения двух прямых (производная точка)
- Производные объекты: пунктирный стиль, светящийся ободок,
  автообновление при перемещении родительских точек
- Двухфазный UI для parallel/perpendicular/intersect:
  _pendingLineRef + _drawLineRefHighlight (подсветка первой линии)
- lab.html: 6 новых кнопок в секции "Построения", счётчик построений,
  onHintChange callback для контекстных подсказок

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 10:00:00 +03:00
Maxim Dolgolyov 35849cf231 feat: планиметрия — интерактивная геометрическая симуляция
- Новый файл frontend/js/labs/geometry.js (~1200 строк):
  GeoEngine (граф объектов с каскадным удалением),
  GeoViewport (система координат math↔canvas, зум/пан),
  GeoSim (полный движок: точки, отрезки, прямые, лучи,
  окружности, треугольники, многоугольники, привязка к сетке
  и точкам, undo/redo, экспорт PNG, classroom sync)
- frontend/lab.html: карточка, ctrl, sim-geometry секция,
  функции geoSetTool/geoToggle/_openGeometry, скрипт-тег
- frontend/admin.html: geometry в ADMIN_SIMS
- backend/src/db/migrate.js: таблицы geometry_tasks,
  geometry_submissions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 09:40:41 +03:00
Maxim Dolgolyov b946a6a187 feat: скрывать отключённые/недоступные модули с galaxy map по feature flags 2026-04-14 09:02:30 +03:00
Maxim Dolgolyov e283368111 feat: galaxy map — интерактивная карта модулей на canvas для /sitemap 2026-04-14 08:58:35 +03:00
Maxim Dolgolyov dfb7c75fbf feat: постраничная навигация по главам в teacher-guide (showChapter + hash-роутинг) 2026-04-14 08:51:06 +03:00
Maxim Dolgolyov 8317a991c4 style: убрать статистику из hero teacher-guide 2026-04-14 08:48:09 +03:00
Maxim Dolgolyov 7f8838515a feat: интерактивное руководство для учителей /teacher-guide + ссылка в сайдбаре 2026-04-14 08:47:06 +03:00
Maxim Dolgolyov cf07fb9017 fix: sitemap — светлая тема вместо тёмной, соответствует стилю платформы
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 08:34:37 +03:00
Maxim Dolgolyov bc2e3909bb feat: страница-путеводитель /sitemap для новых учеников
Интерактивная карта всех модулей платформы:
- Hero с анимированными blob-элементами и gradient-заголовком
- Sticky filter bar (Учёба / Практика / Игры / Личное)
- Маршрут новичка (Дашборд → Теория → Задания)
- 16 карточек модулей с accent-цветами по категориям
- Staggered entrance через IntersectionObserver
- Тёмная тема, мобильная адаптация
- Ссылка в sidebar.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 08:31:07 +03:00
Maxim Dolgolyov c5ae7c2c36 docs: добавить правило — коммит и push после любых изменений
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 21:29:09 +03:00
Maxim Dolgolyov 0ac292ab9e refactor: move emitToSession broadcast into ws-server.broadcastToSession
Consolidates WS-first + SSE-fallback + guest forwarding logic
into ws-server.js so classroomController doesn't duplicate delivery.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 21:27:14 +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 074ee5687b docs: add comprehensive README
Covers all features, quick start (Docker + manual), architecture,
API overview, roles, env vars, and technical decisions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 10:30:38 +03:00
Maxim Dolgolyov f4eee2af3f feat: minimap navigation overlay + ruler/protractor property controls
Minimap:
- Auto-shows in bottom-right corner when zoom > 1.05
- Renders full board content at scale (background + all strokes)
- Purple viewport indicator with darkened outer areas
- Click/drag to jump-pan the viewport
- Cleaned up on destroy()

Ruler/protractor property controls:
- Rotation handle (purple ↺) — drag to rotate around origin
- Resize handle (cyan ↔) — drag to change length/radius
- Protractor now supports rotation via ctx.rotate(ov.angle)
- Floating props panel in toolbar: angle° and length/radius inputs
- Panel auto-shows on first click/drag, hides when overlay toggled off
- Canvas-space hit testing with rotation-aware local coordinates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 10:12:27 +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