Three changes to singlePlayerPage:
1. Store creation no longer waits for isEditorReady. The editor ref
is already passed as optional to NavigationSnapshotService and
loadNodeSnapshotFromDatabase, so we can create and populate the
store without needing the editor to be mounted first.
2. <Tldraw> only renders when store is defined: {store && <Tldraw>}.
Previously it rendered immediately with store={undefined}, which
caused TLDraw to initialize with no backing store and crash
reading topOffset on an unresolved internal object.
3. Loading guard also checks !user so the component never renders
with a null profile before the redirect effect fires.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous implementation had two concurrent session recovery paths:
1. loadInitialSession() calling supabase.auth.getSession()
2. onAuthStateChange handling INITIAL_SESSION/SIGNED_IN
These raced unpredictably causing setLoading(false) to never fire in
certain interleavings, leaving the app permanently stuck on the spinner.
Fix: Remove loadInitialSession() entirely. Start loading=true. Rely
solely on onAuthStateChange — INITIAL_SESSION fires immediately with
the current session state, eliminating the race. One path, one
setLoading(false) call, no interleaving possible.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vite hashes all JS/CSS assets, so those can be cached for 30 days.
But index.html must never be cached — it references the current hashes.
Caching index.html meant users ran old JS after deploys.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed NeoUserContext and NeoInstituteContext from the gateway
spinner. They were blocking ALL authenticated routes until Neo4j
graph data was loaded, which took 1-8 seconds and caused persistent
spinners. Now only UserContext (Supabase profile, ~200ms) must be
ready before routes render. Each page handles its own graph data
loading state independently.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
singlePlayerPage was using useUser().user which is always null (no setter
in UserContext). This caused the redirect effect to always fire, making
the workspace completely inaccessible. Fixed by destructuring profile as
user, which IS properly set from the Supabase profiles query.
Also added 8s timeout to NeoUserContext.switchContext call to match the
NeoInstituteContext timeout fix, preventing infinite spinner if the
navigation API call hangs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Races the SchoolNeoDBService.getSchoolNode() call against an 8-second
timeout. If Neo4j is slow or unavailable the workspace now loads within
seconds rather than waiting for the full axios 120s timeout. The context
degrades gracefully — workspace opens without institute data, error is
logged as a warning not an exception.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reads VITE_TLSYNC_SECRET from env and appends ?token=... to the
/connect/:roomId WebSocket URI so tlsync server can authenticate connections.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- KeywordWatch and KeywordMatch interfaces in transcriptionStore
- loadKeywordWatches, addKeywordWatch, deleteKeywordWatch actions via API with JWT auth
- checkSegmentForKeywords: client-side detection on each final segment, logs events to backend
- clearKeywordMatches: resets session-scoped match list
- Keywords tab in CCTranscriptionPanel: add/delete watches, match log with timestamp
- Match count badge on Keywords tab when hits exist during recording
- Also fixes missing Close import that was present in summary modal
- Connect transcriptionStore to Supabase (start/stop session, save segments)
- Add CanvasEventLogger for silent TLDraw activity tracking
- Add Sessions tab to CCTranscriptionPanel with past sessions list
- Auto-detect timetable context on panel mount
- Flush canvas events to API every 5 seconds during recording
- Add CCTranscriptionPanel component with Live tab
- Add Zustand transcriptionStore for session state management
- Wire panel into BasePanel sidebar system
- Fix merged switch cases in getIconForPanel, getDescriptionForPanel, renderCurrentPanel
- Add VITE_WHISPERLIVE_URL to .env
- timetableOnlyService: timetable-specific methods (line 134)
- timetableService: combined export with class/lesson/enrollment methods (line 328)
- This fixes 'getClasses is not a function' and 'getMyClasses is not a function' errors
- Header.tsx: Use <MenuIcon /> instead of <Menu /> component
- NotFoundPublic.tsx: Use <ErrorOutlineIcon /> instead of <ErrorOutline />
Resolves: Menu open prop error and ErrorOutline undefined error
- Update vite.config.ts HMR host to 192.168.0.94 for container dev
- Remove obsolete .env.dev and .env.prod files
- These old env files contained outdated anon keys causing 401 errors
- .env.development is now the single source of truth for dev config
- Fixed HMR to use WSS when behind SSL proxy
- Fixed define block - values now properly JSON.stringify'd
- Changed server.host to '0.0.0.0' for proper container binding
- Created .env.development for automatic env loading
- Uses loadEnv() to properly load environment files