import { useState, useEffect, useRef } from 'react' interface PythonSetupProps { onReady: () => void } interface SetupProgress { status: 'downloading' | 'extracting' | 'complete' | 'error' percent: number downloadedBytes: number totalBytes: number speed: number } const formatBytes = (bytes: number): string => { if (bytes === 0) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}` } const formatTimeRemaining = (seconds: number): string => { if (!seconds || !isFinite(seconds) || seconds <= 0) return '--' if (seconds < 60) return `${Math.round(seconds)}s` if (seconds < 3600) return `${Math.round(seconds / 60)}m` return `${Math.round(seconds / 3600)}h ${Math.round((seconds % 3600) / 60)}m` } export function PythonSetup({ onReady }: PythonSetupProps) { const [progress, setProgress] = useState(null) const [error, setError] = useState(null) const [videoPath, setVideoPath] = useState('/splash/splash.mp4') const started = useRef(false) const getTimeRemaining = (): string => { if (!progress || progress.speed <= 0) return '--' const remainingBytes = progress.totalBytes - progress.downloadedBytes if (remainingBytes <= 0) return '--' return formatTimeRemaining(remainingBytes / progress.speed) } // Resolve video path for production useEffect(() => { const init = async () => { try { const resourcePath = await window.electronAPI.getResourcePath?.() if (resourcePath) { setVideoPath(`file://${resourcePath}/app.asar.unpacked/dist/splash/splash.mp4`) } } catch { // Dev mode: use relative path } } init() }, []) // Listen for progress events useEffect(() => { window.electronAPI.onPythonSetupProgress((data: unknown) => { const p = data as SetupProgress setProgress(p) if (p.status === 'complete') { onReady() } else if (p.status === 'error') { setError('Download failed. Please check your internet connection and try again.') } }) return () => { window.electronAPI.removePythonSetupProgress() } }, [onReady]) // Start download on mount useEffect(() => { if (started.current) return started.current = true startSetup() }, []) const startSetup = async () => { setError(null) try { await window.electronAPI.startPythonSetup() } catch (e) { setError(e instanceof Error ? e.message : 'Failed to download Python environment.') } } const statusLabel = progress?.status === 'extracting' ? 'Extracting...' : 'Downloading Python environment...' const percent = progress?.percent ?? 0 return (
{/* Custom Title Bar */}
LTX Desktop
{/* Main Container */}
{/* Header */}
{/* Content Area */}
{/* Video Section */}
{/* Progress Section */}
{error ? (
{error}
) : ( <> {/* Header row */}
{statusLabel} {percent}%
{/* Progress Bar */}
{/* Stats row */}
{progress?.status === 'extracting' ? 'Setting up Python environment...' : 'First-time setup — this only happens once'}
{progress && progress.speed > 0 && ( {(progress.speed / (1024 * 1024)).toFixed(1)} MB/s )} {progress && progress.downloadedBytes > 0 && ( {formatBytes(progress.downloadedBytes)}{progress.totalBytes > 0 ? ` / ${formatBytes(progress.totalBytes)}` : ''} )} {progress && progress.speed > 0 && progress.totalBytes > 0 && ( ETA: {getTimeRemaining()} )}
)}
{/* Footer */}
© 2026 Lightricks
) }