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>
This commit is contained in:
Maxim Dolgolyov
2026-04-13 18:04:59 +03:00
parent 074ee5687b
commit fd29acbbdd
70 changed files with 12231 additions and 498 deletions
+25 -1
View File
@@ -154,7 +154,31 @@
"Bash(powershell -Command \"Stop-Process -Id 69696 -Force\")",
"Bash(powershell -Command \"Start-Sleep 1\")",
"Bash(powershell -Command \"\\(Get-NetTCPConnection -LocalPort 3000 -State Listen -ErrorAction SilentlyContinue\\).OwningProcess\")",
"Bash(powershell -Command \"Stop-Process -Id 10880 -Force\")"
"Bash(powershell -Command \"Stop-Process -Id 10880 -Force\")",
"Bash(grep -v '\\\\.js$')",
"Bash(curl -s -X POST http://localhost:3000/api/auth/login -H \"Content-Type: application/json\" -d '{\"login\":\"admin\",\"password\":\"admin123\"}')",
"Bash(curl -s -w '\\\\nHTTP_STATUS:%{http_code}' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Miwicm9sZSI6ImFkbWluIiwidmVyc2lvbiI6MCwiaWF0IjoxNzc1OTgyNzc2LCJleHAiOjE3NzYwNjkxNzZ9.FJ3Ya9X_Qg5fEUagPc1l8KrDnj2BaKrXarA-KRVr_QM' http://localhost:3000/api/classroom/6/pages)",
"Bash(curl -s -w '\\\\nHTTP:%{http_code}' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Miwicm9sZSI6ImFkbWluIiwidmVyc2lvbiI6MCwiaWF0IjoxNzc1OTgyNzc2LCJleHAiOjE3NzYwNjkxNzZ9.FJ3Ya9X_Qg5fEUagPc1l8KrDnj2BaKrXarA-KRVr_QM' 'http://localhost:3000/api/classroom/6/strokes?page_num=1')",
"Bash(wmic process:*)",
"Bash(taskkill /F /PID 67276)",
"Bash(cmd /c \"taskkill /F /PID 67276\")",
"Bash(cmd /c \"taskkill /F /PID 67276 && echo killed\")",
"Bash(cmd /c \"wmic process where ProcessId=67276 delete\")",
"Bash(powershell -Command \"Stop-Process -Id 67276 -Force\")",
"Bash(powershell -Command \"Start-Sleep -Milliseconds 1500; \\(Invoke-WebRequest -Uri 'http://localhost:3000/api/health' -UseBasicParsing\\).Content\")",
"Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:3000/lesson-history)",
"Bash(curl -s http://localhost:3000/api/classroom/my/history -H \"Authorization: Bearer test\")",
"Bash(curl -s -X DELETE http://localhost:3000/api/classroom/999/history -H \"Authorization: Bearer bad\")",
"Bash(pkill -f \"node.*server\")",
"Bash(grep -n \"</div>.*app-layout\\\\|<!-- app-layout\\\\|^</div>$\" frontend/classroom.html)",
"Bash(taskkill /PID 1336 /F)",
"Bash(taskkill /PID 55392 /F)",
"Bash(taskkill /PID 60564 /F)",
"Bash(ping -n 3 127.0.0.1)",
"Bash(cmd /c \"taskkill /PID 60564 /F\")",
"Bash(cmd /c \"taskkill /F /PID 60564 2>&1\")",
"Bash(kill -9 60564)",
"Bash(kill -9 9313)"
],
"additionalDirectories": [
"\\tmp"