import React, { useState, useEffect, useRef, useCallback } from 'react'; const COLLECT_MS = 2000; const CENTER_MS = 3000; // centre point gets extra time (bias reference) function CalibrationOverlay({ calibration, videoManager }) { const [progress, setProgress] = useState(0); const timerRef = useRef(null); const startRef = useRef(null); const overlayRef = useRef(null); const enterFullscreen = useCallback(() => { const el = overlayRef.current; if (!el) return; const req = el.requestFullscreen || el.webkitRequestFullscreen || el.msRequestFullscreen; if (req) req.call(el).catch(() => {}); }, []); const exitFullscreen = useCallback(() => { if (document.fullscreenElement || document.webkitFullscreenElement) { const exit = document.exitFullscreen || document.webkitExitFullscreen || document.msExitFullscreen; if (exit) exit.call(document).catch(() => {}); } }, []); useEffect(() => { if (calibration && calibration.active && !calibration.done) { const t = setTimeout(enterFullscreen, 100); return () => clearTimeout(t); } }, [calibration?.active]); useEffect(() => { if (!calibration || !calibration.active) exitFullscreen(); }, [calibration?.active]); useEffect(() => { if (!calibration || !calibration.collecting || calibration.done) { setProgress(0); if (timerRef.current) cancelAnimationFrame(timerRef.current); return; } startRef.current = performance.now(); const duration = calibration.index === 0 ? CENTER_MS : COLLECT_MS; const tick = () => { const pct = Math.min((performance.now() - startRef.current) / duration, 1); setProgress(pct); if (pct >= 1) { if (videoManager) videoManager.nextCalibrationPoint(); startRef.current = performance.now(); setProgress(0); } timerRef.current = requestAnimationFrame(tick); }; timerRef.current = requestAnimationFrame(tick); return () => { if (timerRef.current) cancelAnimationFrame(timerRef.current); }; }, [calibration?.index, calibration?.collecting, calibration?.done]); const handleCancel = () => { if (videoManager) videoManager.cancelCalibration(); exitFullscreen(); }; if (!calibration || !calibration.active) return null; if (calibration.done) { return (
{calibration.success ? 'Gaze tracking is now active.' : 'Not enough samples collected. Try again.'}