import { useEffect, useRef, useState } from 'react'; import { copy } from '@/shared/i18n'; import { cn } from '@/shared/lib/cn'; import type { HudStatusLinePayload } from '@/shared/lib/useHudStatusLine'; import { ExitHoldButton } from '@/features/exit-hold'; import { GoalCompleteSheet } from './GoalCompleteSheet'; import { InlineMicrostep } from './InlineMicrostep'; import { ThoughtOrb } from './ThoughtOrb'; interface SpaceFocusHudWidgetProps { sessionId?: string | null; goal: string; microStep?: string | null; remainingSeconds?: number | null; phaseStartedAt?: string | null; timeDisplay?: string; hasActiveSession?: boolean; playbackState?: 'running' | 'paused'; sessionPhase?: 'focus' | 'break' | null; onIntentUpdate: (payload: { goal?: string; microStep?: string | null }) => boolean | Promise; onGoalUpdate: (nextGoal: string) => boolean | Promise; onGoalFinish: () => boolean | Promise; onTimerFinish: () => boolean | Promise; onAddTenMinutes: () => boolean | Promise; onStatusMessage: (payload: HudStatusLinePayload) => void; onCaptureThought: (note: string) => void; onExitRequested: () => void; } export const SpaceFocusHudWidget = ({ sessionId = null, goal, microStep, remainingSeconds = null, phaseStartedAt = null, timeDisplay, hasActiveSession = false, playbackState = 'paused', sessionPhase = 'focus', onIntentUpdate, onGoalUpdate, onGoalFinish, onTimerFinish, onAddTenMinutes, onStatusMessage, onCaptureThought, onExitRequested, }: SpaceFocusHudWidgetProps) => { const [overlay, setOverlay] = useState<'none' | 'complete' | 'timer-complete'>('none'); const [completePreferredView, setCompletePreferredView] = useState<'choice' | 'next'>('choice'); const [isSavingIntent, setSavingIntent] = useState(false); const visibleRef = useRef(false); const timerPromptSignatureRef = useRef(null); const normalizedGoal = goal.trim().length > 0 ? goal.trim() : copy.space.focusHud.goalFallback; const isCompleteOpen = overlay === 'complete' || overlay === 'timer-complete'; const timerCompletionSignature = hasActiveSession && sessionPhase === 'focus' && remainingSeconds === 0 && phaseStartedAt ? `${sessionId ?? 'session'}:${phaseStartedAt}` : null; useEffect(() => { if (!hasActiveSession) { setOverlay('none'); setSavingIntent(false); setCompletePreferredView('choice'); timerPromptSignatureRef.current = null; } }, [hasActiveSession]); useEffect(() => { if (!visibleRef.current && playbackState === 'running') { onStatusMessage({ message: copy.space.focusHud.goalToast(normalizedGoal), }); } visibleRef.current = true; }, [normalizedGoal, onStatusMessage, playbackState]); useEffect(() => { if (!timerCompletionSignature) { return; } if (timerPromptSignatureRef.current === timerCompletionSignature) { return; } timerPromptSignatureRef.current = timerCompletionSignature; setOverlay('timer-complete'); }, [timerCompletionSignature]); const handleOpenCompleteSheet = (preferredView: 'choice' | 'next' = 'choice') => { setCompletePreferredView(preferredView); setOverlay('complete'); }; const handleInlineMicrostepUpdate = async (nextStep: string | null) => { if (isSavingIntent) return false; setSavingIntent(true); try { const didUpdate = await onIntentUpdate({ microStep: nextStep }); if (didUpdate) { if (nextStep) { onStatusMessage({ message: copy.space.focusHud.refocusSaved }); } else { onStatusMessage({ message: copy.space.focusHud.microStepCleared }); } } return didUpdate; } finally { setSavingIntent(false); } }; return ( <>
{/* The Monolith (Central Hub) */}
{/* Massive Unstoppable Timer */}

{timeDisplay}

{/* Core Intent */}
{/* Immutable Goal */}

{normalizedGoal}

{/* Kinetic Inline Microstep */}
{hasActiveSession && (sessionPhase === 'focus' || sessionPhase === 'break') && ( )}
setOverlay('none')} onFinish={() => overlay === 'timer-complete' ? Promise.resolve(onTimerFinish()) : Promise.resolve(onGoalFinish()) } onExtendTenMinutes={() => Promise.resolve(onAddTenMinutes())} onRest={() => { setOverlay('none'); // The timer doesn't pause, they just rest within the flow. }} onConfirm={(nextGoal) => { return overlay === 'timer-complete' ? Promise.resolve(false) : Promise.resolve(onGoalUpdate(nextGoal)); }} />
{/* Emergency Tether (Exit) */}
); };