All files / web/src/components/tutorial TutorialUIContext.tsx

82.19% Statements 60/73
75% Branches 3/4
50% Functions 2/4
82.19% Lines 60/73

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 741x 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 207x 207x 207x 207x 207x 207x 207x 207x 207x 207x 207x 207x 207x 207x 165x 165x 165x 165x 165x                   165x     165x 165x 165x 207x 207x 207x 207x 207x 1x 1x 180x 180x     180x 180x  
'use client'
 
import type React from 'react'
import { createContext, useContext, useMemo, useState } from 'react'
import type { PedagogicalSegment } from './DecompositionWithReasons'
 
type HintFocus = 'none' | 'term' | 'bead'
 
interface TutorialUIState {
  showCoachBar: boolean
  setShowCoachBar: (v: boolean) => void
  canHideCoachBar: boolean
 
  // Single-owner tooltip gate (tutorial-only)
  hintFocus: HintFocus
  requestFocus: (who: HintFocus) => boolean // returns true if granted
  releaseFocus: (who: HintFocus) => void
 
  // Currently active segment for Coach Bar
  activeSegment: PedagogicalSegment | null
  setActiveSegment: (seg: PedagogicalSegment | null) => void
}
 
const TutorialUIContext = createContext<TutorialUIState | undefined>(undefined)
 
export function TutorialUIProvider({
  children,
  initialSegment = null,
  canHideCoachBar = true,
}: {
  children: React.ReactNode
  initialSegment?: PedagogicalSegment | null
  canHideCoachBar?: boolean
}) {
  const [showCoachBar, setShowCoachBar] = useState(true)
  const [hintFocus, setHintFocus] = useState<HintFocus>('none')
  const [activeSegment, setActiveSegment] = useState<PedagogicalSegment | null>(initialSegment)
 
  const value: TutorialUIState = useMemo(
    () => ({
      showCoachBar,
      setShowCoachBar,
      canHideCoachBar,
      hintFocus,
      requestFocus: (who: HintFocus) => {
        if (hintFocus === 'none' || hintFocus === who) {
          setHintFocus(who)
          return true
        }
        if (process.env.NODE_ENV !== 'production') {
          console.debug(`[tutorial-ui] focus denied: ${who}, owned by ${hintFocus}`)
        }
        return false
      },
      releaseFocus: (who: HintFocus) => {
        if (hintFocus === who) setHintFocus('none')
      },
      activeSegment,
      setActiveSegment,
    }),
    [showCoachBar, canHideCoachBar, hintFocus, activeSegment]
  )
 
  return <TutorialUIContext.Provider value={value}>{children}</TutorialUIContext.Provider>
}
 
export function useTutorialUI(): TutorialUIState {
  const ctx = useContext(TutorialUIContext)
  if (!ctx) {
    throw new Error('useTutorialUI must be used within <TutorialUIProvider> (tutorial routes)')
  }
  return ctx
}