import { useEffect, useRef, useState } from 'react' import { useStore } from '@nanostores/react' import { Button } from '../components/button' import { cancelInstall, $progress, type BootstrapStateModel, type StageState } from '../store' import { Check, X, ChevronRight, FileText, Loader2 } from 'lucide-react' import clsx from 'clsx' interface ProgressProps { bootstrap: BootstrapStateModel } /* * Progress screen — drives a stage list + collapsible log panel. Uses * the DS for the top bar so its motion + ring match the rest * of the product. */ export default function ProgressScreen({ bootstrap }: ProgressProps) { const progress = useStore($progress) const [showLogs, setShowLogs] = useState(false) const logEndRef = useRef(null) useEffect(() => { if (showLogs && logEndRef.current) { logEndRef.current.scrollIntoView({ behavior: 'smooth' }) } }, [bootstrap.logs.length, showLogs]) const currentStage = bootstrap.currentStage != null ? bootstrap.stages[bootstrap.currentStage] : null return (
{bootstrap.status === 'running' && ( )} {bootstrap.status === 'running' ? currentStage ? currentStage.info.title : 'Preparing\u2026' : bootstrap.status === 'completed' ? 'Done' : 'Installing'}
{progress.done} of {progress.total} steps
{/* Top progress bar — plain HTML, derived from --primary so it tracks the theme accent. */}
    {bootstrap.stageOrder.map((name) => { const rec = bootstrap.stages[name] if (!rec) return null return (
  1. {rec.info.title} {rec.durationMs != null && ( {formatDuration(rec.durationMs)} )}
  2. ) })}
{showLogs && (
Live output
{bootstrap.logs.length} lines
{bootstrap.logs.map((entry, idx) => (
{entry.line}
))}
)}
{bootstrap.status === 'running' && ( )}
) } function StateIcon({ state }: { state: StageState | null }) { if (state === 'running') { return } if (state === 'succeeded') { return } if (state === 'skipped') { return } if (state === 'failed') { return } return (
) } function formatDuration(ms: number): string { if (ms < 1000) return `${ms}ms` if (ms < 60000) return `${(ms / 1000).toFixed(1)}s` const m = Math.floor(ms / 60000) const s = Math.round((ms % 60000) / 1000) return `${m}m ${s}s` }