import React, { createContext, useContext, useState, useSyncExternalStore } from 'react'; import { createStore, type Store } from '../state/store.js'; export type VoiceState = { voiceState: 'idle' | 'recording' | 'processing'; voiceError: string | null; voiceInterimTranscript: string; voiceAudioLevels: number[]; voiceWarmingUp: boolean; }; const DEFAULT_STATE: VoiceState = { voiceState: 'idle', voiceError: null, voiceInterimTranscript: '', voiceAudioLevels: [], voiceWarmingUp: false, }; type VoiceStore = Store; export const VoiceContext = createContext(null); type Props = { children: React.ReactNode; }; export function VoiceProvider({ children }: Props): React.ReactNode { // Store is created once — stable context value means the provider never // triggers re-renders. Consumers subscribe to slices via useVoiceState. const [store] = useState(() => createStore(DEFAULT_STATE)); return {children}; } function useVoiceStore(): VoiceStore { const store = useContext(VoiceContext); if (!store) { throw new Error('useVoiceState must be used within a VoiceProvider'); } return store; } /** * Subscribe to a slice of voice state. Only re-renders when the selected * value changes (compared via Object.is). */ export function useVoiceState(selector: (state: VoiceState) => T): T { const store = useVoiceStore(); const get = () => selector(store.getState()); return useSyncExternalStore(store.subscribe, get, get); } /** * Get the voice state setter. Stable reference — never causes re-renders. * store.setState is synchronous: callers can read getVoiceState() immediately * after to observe the new value (VoiceKeybindingHandler relies on this). */ export function useSetVoiceState(): (updater: (prev: VoiceState) => VoiceState) => void { return useVoiceStore().setState; } /** * Get a synchronous reader for fresh state inside callbacks. Unlike * useVoiceState (which subscribes), this doesn't cause re-renders — use * inside event handlers that need to read state set earlier in the same tick. */ export function useGetVoiceState(): () => VoiceState { return useVoiceStore().getState; }