Add useNavigate + Launch icon to TreeItem; timetable section shows a
launch button routing to /my-lessons, classes section routes to /my-classes.
Both buttons only appear when the section status is populated.
Exclude classes section from the unlinked/pending icon fallback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bugs fixed:
- 'cc-teacher-timetable-node' missing from NODE_TYPE_THEMES caused
Cannot read properties of undefined (reading 'headerColor') crash
when clicking My Timetable
- 'cc-journal-node' and 'cc-planner-node' had no shape utils registered,
causing 'No shape util found for type' error on Journal/Planner click
- Added null safety (?? fallback) to getNodeTheme to prevent future crashes
from any other unmapped type
- Removed AcademicWeek from canExpand exclusion so weeks can be expanded
to show individual academic days
Added:
- CCJournalNodeShapeUtil and CCPlannerNodeShapeUtil (stub shapes)
- CCJournalNodeProps and CCPlannerNodeProps types
- Journal/Planner added to CCNodeTypes, ccGraphShapeProps, NODE_TYPE_THEMES
- 'cc-department-structure-node' mapping added to NODE_TYPE_THEMES
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SchoolOnboardingWizard.tsx: new 3-step wizard (GAIS search → confirm →
calendar setup) triggered from the nav panel school section when no school
is linked. Calls GET /school/search for live school lookup and
POST /school/register to create the institute record.
- CCGraphNavPanel.tsx: add showOnboard button for no_school state,
onOnboardSchool context action, wire up SchoolOnboardingWizard state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
navigationStore: rewritten off Neo4j db names — Supabase whiteboard_rooms table,
setAuthInfo(token, userId) pattern, auto-creates default room per context on first use
snapshotService: rewritten to Supabase Storage REST (/storage/v1/object/authenticated/cc.users/…),
setAccessToken() instance method, static methods take accessToken not dbName
AuthContext/NeoUserContext: auth injected into nav store, no Neo4j db names required
singlePlayerPage: loadNodeData no longer calls Neo4j; snapshot wired via accessToken
navigation types: NeoGraphNode updated for Supabase-backed tree structure
transcriptionStore/Service: getSession() removed, accessToken via AuthContext
LLMConfigModal: auth context wiring fixes
GraphNavigator/GraphSidebar: updated nav components
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same GoTrueClient lock contention fix as CCFilesPanel:
- transcriptionStore: add _accessToken/_userId state + setAuthInfo() action;
replace all 6 getSession() calls (startSession, flushCanvasEvents, loadSessions,
loadKeywordWatches, addKeywordWatch, deleteKeywordWatch, checkSegmentForKeywords)
with stored values — zero getSession() calls remain in the store
- CCTranscriptionPanel: destructure accessToken from useAuth; sync both values into
store via setAuthInfo() on every auth change; gate loadSessions on authUser.id
- CCCabinetsPanel: same pattern as CCFilesPanel — useAuth for token, useCallback on
apiFetch/loadCabinets, gate effect on authUser.id
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: apiFetch called supabase.auth.getSession() on every API request, acquiring
the GoTrueClient internal lock. On refresh, concurrent mount of panels caused lock
contention — the loadCabinets/loadSessions fetch awaits hung, loading spinner never
cleared.
- AuthContext: add accessToken state, set/clear it alongside user on all auth events
- CCFilesPanel: apiFetch reads accessToken from AuthContext (no lock), loadCabinets
effect gated on authUser.id (fires only after SIGNED_IN, session guaranteed valid)
- CCTranscriptionPanel: loadSessions effect gated on authUser.id for same reason
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- transcriptionStore: replace all supabase.auth.getUser() with getSession() so session
restoration on page refresh does not race against GoTrue network validation
- CCFilesPanel: remove selectedCabinet from loadCabinets useCallback deps; use
initialSelectionDone ref to prevent double-call on first mount
- CCTranscriptionPanel: replace hardcoded LAN IP with VITE_API_URL env var
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root causes of disconnection and slow transcription:
- AudioWorklet was firing every 128 native samples (~48 kHz), sending
~375 tiny WebSocket messages/sec. Server flooded with tiny frames
during silence → keepalive ping timed out → connection dropped.
- JS resampling 48 kHz → 16 kHz added CPU overhead on every chunk.
- Audio started on ws.onopen before server sent SERVER_READY, so early
frames were dropped.
Fixes:
- audioWorklet.js: accumulate 4096 samples before posting (256 ms/chunk
at 16 kHz, ~4 messages/sec), transfer ArrayBuffer zero-copy.
- transcriptionService: AudioContext({ sampleRate: 16000 }) — browser
handles native resampling, no JS resampling needed. Remove
resampleTo16kHZ entirely.
- Wait for SERVER_READY message before calling setupAudioProcessing().
- Send 'END_OF_AUDIO' string on stop so server can finalise last segment.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- transcriptionService: track finalizedSegmentCount so only newly-final
segments are emitted per WS message (was re-processing full array each
time, causing the live segment to freeze in the completed list)
- transcriptionStore: saveSegment isFinal branch now appends the passed
text directly instead of currentSegment (currentSegment was stale
relative to the incoming final)
- CCTranscriptionPanel: record button colour changed from var(--color-text)
to explicit #2563eb so it is visible in dark mode; completed segment
backgrounds changed from hardcoded #fff to var(--color-muted) so text
is readable in both themes; keyword Add button gets same blue fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The old startTranscription() called enumerateDevices() first to find a
device ID, then getUserMedia. Without microphone permission, enumerateDevices()
returns devices with deviceId="" (empty string, falsy). This caused the
!this.selectedDeviceId check to bail out early, never calling getUserMedia,
never prompting the user for mic permission, and never creating the WebSocket.
Result: user clicks Start Recording → isRecording=true → button changes to
Stop Recording → but no mic prompt, no WebSocket, no transcription.
Fix: call getUserMedia directly (with optional echoCancellation/noiseSuppression
if no device pre-selected). getUserMedia triggers the browser permission prompt
automatically. Device selection is still honoured via exact deviceId constraint
when one has been explicitly chosen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BasePanel reads PANEL_DIMENSIONS[currentPanelType] to get the panel
dimensions (topOffset, bottomOffset, width). The transcription panel
type was added to the panel list and render switch during CIS Phase 3C
but was never added to PANEL_DIMENSIONS. This caused a crash whenever
the BasePanel rendered with the transcription tab active:
PANEL_DIMENSIONS["transcription"] === undefined
undefined.topOffset → TypeError: Cannot read properties of undefined
This crash appeared as a topOffset error anywhere a Tldraw canvas was
rendered (singlePlayerPage, multiplayerUser, etc.).
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