import React from 'react'; import { CheckCircle, AlertTriangle, XCircle, Loader } from 'lucide-react'; import { usePreflight, useModelStatus } from '../api/hooks'; import './ReadinessChecklist.css'; /** * ReadinessChecklist — system readiness panel. * * Consumes the existing /setup/preflight endpoint (OS, RAM, GPU, ffmpeg, * yt-dlp, network) plus /model/status, and renders a compact pass/warn/fail * checklist. Mirrors into Settings and renders as empty-state on the * launchpad when no project is loaded. * * Hides itself when all gates are green (user doesn't need to see * "everything is fine" every time they open the app). */ const StatusIcon = ({ status, size = 14 }) => { switch (status) { case 'pass': return ; case 'warn': return ; case 'fail': return ; case 'loading': return ; default: return ; } }; export default function ReadinessChecklist({ compact = false, showWhenAllPass = false }) { const { data: preflight, isLoading: preflightLoading } = usePreflight(); const { data: modelData, isLoading: modelLoading } = useModelStatus(); const isLoading = preflightLoading || modelLoading; const modelStatus = modelData?.status ?? 'idle'; // Build the checklist from preflight data + model status const checks = []; // Model readiness (from /model/status) const modelDetail = modelData?.detail || ''; const modelErr = modelData?.error || null; const modelCheck = { id: 'asr-model', label: 'ASR Model', status: modelStatus === 'ready' ? 'pass' : modelStatus === 'loading' ? 'loading' : modelStatus === 'error' || modelData?.sub_stage === 'error' ? 'fail' : 'warn', detail: modelStatus === 'ready' ? 'Loaded and ready' : modelStatus === 'loading' ? (modelDetail || 'Loading… (this may take 1-2 minutes on first run)') : (modelData?.sub_stage === 'error' ? (modelErr || 'Failed to load') : 'Not loaded yet — will load on first transcription'), fix: (modelStatus === 'error' || modelData?.sub_stage === 'error') ? (modelErr ? `Error: ${modelErr}. Check logs and try restarting.` : 'Check logs for model loading errors. Try restarting.') : null, }; checks.push(modelCheck); // Add preflight checks if (preflight?.checks) { // Filter to the most relevant checks for the checklist const relevant = ['gpu', 'ffmpeg', 'yt-dlp', 'ram']; for (const check of preflight.checks) { if (relevant.includes(check.id)) { checks.push(check); } } } // LLM configuration (check for translate endpoint) const llmCheck = { id: 'llm', label: 'LLM (Cinematic)', status: 'warn', detail: 'Configure TRANSLATE_BASE_URL for Cinematic translation quality', fix: 'Set TRANSLATE_BASE_URL and TRANSLATE_API_KEY environment variables. Works with Ollama, OpenAI, LM Studio, etc.', }; // If we have preflight and there's a network check passing, LLM is at least possible if (preflight?.checks) { const netCheck = preflight.checks.find(c => c.id === 'network'); if (netCheck?.status === 'pass') { llmCheck.detail = 'Optional — set TRANSLATE_BASE_URL for Cinematic quality'; } } checks.push(llmCheck); // Determine if all critical checks pass const allPass = checks.every(c => c.status === 'pass' || c.status === 'warn'); const anyFail = checks.some(c => c.status === 'fail'); const criticalFails = checks.filter(c => c.status === 'fail'); // Hide when everything is fine (unless explicitly asked to show) if (!showWhenAllPass && allPass && !isLoading) return null; if (isLoading) { return (
🔍 Checking system…
); } if (compact) { // Compact mode: just show failing/warning items const issues = checks.filter(c => c.status !== 'pass'); if (issues.length === 0) { return (
All systems ready
); } return (
    {issues.map(check => (
  • {check.label}
    {check.fix &&
    {check.fix}
    }
  • ))}
); } return (
{anyFail ? '⚠️' : '✅'} System Readiness
    {checks.map(check => (
  • {check.label}
    {check.detail}
    {check.fix &&
    💡 {check.fix}
    }
  • ))}
); }