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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import type { MutableRefObject } from 'react'
import { CONSTANT_IDS, EXPLORATION_DISPLAY } from './talkToNumber/explorationRegistry'
import { NARRATION_CONFIGS } from './constants/demos/narrationConfigs'
const DEMO_DISPLAY = EXPLORATION_DISPLAY
interface Narration {
reset: () => void
stop: () => void
resume: (constantId: string) => void
markTriggered: (constantId: string) => void
isNarrating: MutableRefObject<boolean>
}
interface CreateHandleExploreConstantOptions {
audioManager: { stop: () => void }
voiceStateRef: MutableRefObject<string>
pendingTourRef: MutableRefObject<string | null>
voiceExplorationSegmentRef: MutableRefObject<number>
narration: Narration
cancelDemo: () => void
exitTour: () => void
startTour: () => void
startDemo: (id: string) => void
restoreDemo: (id: string, progress: number) => void
sendSystemMessage: (msg: string, force?: boolean) => void
onSpeedReset: () => void
setTappedConstantId: (v: string | null) => void
setTappedIntValue: (v: number | null) => void
setHoveredValue: (v: number | null) => void
tooltipHoveredRef: MutableRefObject<boolean>
}
export function createHandleExploreConstant({
audioManager,
voiceStateRef,
pendingTourRef,
voiceExplorationSegmentRef,
narration,
cancelDemo,
exitTour,
startTour,
startDemo,
restoreDemo,
sendSystemMessage,
onSpeedReset,
setTappedConstantId,
setTappedIntValue,
setHoveredValue,
tooltipHoveredRef,
}: CreateHandleExploreConstantOptions) {
return (explorationId: string) => {
console.log('[NumberLine] handleExploreConstant — exploration:', explorationId)
audioManager.stop()
// Route tour explorations to the prime tour handler
if (!CONSTANT_IDS.has(explorationId)) {
const onCall = voiceStateRef.current === 'active'
if (onCall) {
// Queue the tour to launch after the call ends — the agent will
// explain the tour, say goodbye, and hang up on its own.
console.log('[NumberLine] queuing tour for after hangup:', explorationId)
pendingTourRef.current = explorationId
} else {
// Not on a call — start the tour immediately
cancelDemo()
setTappedConstantId(null)
setTappedIntValue(null)
setHoveredValue(null)
tooltipHoveredRef.current = false
startTour()
}
return
}
// Constant exploration path
exitTour()
narration.reset()
// Reset speed UI to match (narration.reset() resets the ref + audio rate)
onSpeedReset()
const onCall = voiceStateRef.current === 'active'
console.log(
'[exploration] handleExploreConstant — onCall:',
onCall,
'explorationId:',
explorationId
)
if (onCall) {
// Start paused at progress 0 — narrator introduces first, then calls resume
console.log('[exploration] starting PAUSED (restoreDemo + markTriggered)')
restoreDemo(explorationId, 0)
narration.markTriggered(explorationId) // suppress narration auto-start
} else {
startDemo(explorationId)
}
// If on a voice call, send companion instructions for the exploration
if (onCall) {
const cfg = NARRATION_CONFIGS[explorationId]
const display = DEMO_DISPLAY[explorationId]
if (cfg && display) {
const visualDesc = display.visualDesc
? `\n\nWHAT THE ANIMATION SHOWS (for YOUR reference only — do NOT describe this to the child): ${display.visualDesc}`
: ''
// Companion rules are in the start_exploration tool output (not here)
// so the model won't treat them as a "user message" to recite.
// This message provides constant-specific context only.
console.log('[exploration] sending intro system message + response.create')
sendSystemMessage(
`An animated exploration of ${display.symbol} (${display.name}) is ready to play.` +
`${visualDesc}\n\n` +
`Give the child a brief intro — why this constant is special to you personally. Match the child's energy level. ` +
`Do NOT describe or preview what the animation will show. The visuals should be a surprise. ` +
`Something like "Oh, I've been wanting to show you this!" or "This is one of my favorite things about living near ${display.symbol}." ` +
`Keep it to 1-2 sentences, then call resume_exploration.`,
true // prompt a response so the agent introduces the constant
)
}
voiceExplorationSegmentRef.current = -1
}
}
}
|