Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | /** * Feed narration segment updates to the voice agent during active calls. * * Uses the sequencer's actual segment index (not revealProgress) to gate * cues — prevents premature firing when animation finishes a segment * before the voice agent does. * * Extracted from NumberLine.tsx draw(). */ import type { DemoNarrationConfig } from './constants/demos/useConstantDemoNarration' import { EXPLORATION_DISPLAY, EXPLORATION_RECOMMENDATIONS, } from './talkToNumber/explorationRegistry' export interface VoiceNarrationSyncState { voiceState: string demoPhase: string constantId: string | null revealProgress: number isNarrating: boolean segmentIndex: number lastReportedSegment: number } export interface VoiceNarrationSyncActions { sendSystemMessage: (message: string, promptResponse: boolean) => void setNarrationPlaying: (playing: boolean) => void updateLastReportedSegment: (index: number) => void } /** * Sync narration state to voice agent. Call once per draw frame. * Returns the new lastReportedSegment value. */ export function syncVoiceNarration( state: VoiceNarrationSyncState, actions: VoiceNarrationSyncActions, narrationConfig: DemoNarrationConfig | undefined ): void { if (state.voiceState !== 'active') return if (state.demoPhase === 'idle' || !state.constantId) return if (!narrationConfig) return const segIdx = state.isNarrating ? state.segmentIndex : -1 // Send context-only cue when the sequencer advances to a new segment if (segIdx >= 0 && segIdx !== state.lastReportedSegment) { actions.updateLastReportedSegment(segIdx) const seg = narrationConfig.segments[segIdx] actions.sendSystemMessage( `[Narration playing — DO NOT speak this. The narrator is saying: "${seg.ttsText}"]`, false ) } // Notify when exploration completes if (state.revealProgress >= 1 && state.lastReportedSegment !== narrationConfig.segments.length) { actions.updateLastReportedSegment(narrationConfig.segments.length) actions.setNarrationPlaying(false) const display = EXPLORATION_DISPLAY[state.constantId] const recs = EXPLORATION_RECOMMENDATIONS[state.constantId] ?? [] const recText = recs.length > 0 ? ` If the child seems into it, casually suggest one of these: ${recs .map((r) => { const d = EXPLORATION_DISPLAY[r.id] return `${d?.name ?? r.id} (${r.reason})` }) .join( '; ' )}. Don't list them all — just pick whichever feels most natural for the conversation and mention it casually.` : '' actions.sendSystemMessage( `[The ${display?.name ?? state.constantId} exploration finished. Ask the child what they thought — ` + `brief check-in, then move on.${recText}]`, true ) } } |