feat: integrate SetupWizard into desktop boot flow

SetupWizard orchestrates the pick→connect→ingest→ready state machine with a
step indicator; SetupScreen now transitions to the wizard after all boot checks
pass (phase === 'ready') instead of immediately calling onReady().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
krypticmouse
2026-03-26 18:08:42 +00:00
co-authored by Claude Sonnet 4.6
parent 1b9cdc44c2
commit c299e05e7b
2 changed files with 127 additions and 2 deletions
+8 -2
View File
@@ -1,6 +1,7 @@
import { useState, useEffect, useCallback } from 'react';
import { Loader2, CheckCircle2, XCircle, Cpu, Server, Database } from 'lucide-react';
import { getSetupStatus, type SetupStatus } from '../lib/api';
import { SetupWizard } from './setup/SetupWizard';
const STEPS = [
{ key: 'ollama_ready', label: 'Inference Engine', icon: Cpu, detail: 'Starting Ollama...' },
@@ -70,14 +71,15 @@ function StepRow({
export function SetupScreen({ onReady }: { onReady: () => void }) {
const [status, setStatus] = useState<SetupStatus | null>(null);
const [showWizard, setShowWizard] = useState(false);
const poll = useCallback(async () => {
const s = await getSetupStatus();
if (s) setStatus(s);
if (s?.phase === 'ready') {
setTimeout(onReady, 600);
setTimeout(() => setShowWizard(true), 600);
}
}, [onReady]);
}, []);
useEffect(() => {
poll();
@@ -85,6 +87,10 @@ export function SetupScreen({ onReady }: { onReady: () => void }) {
return () => clearInterval(interval);
}, [poll]);
if (showWizard) {
return <SetupWizard onComplete={onReady} />;
}
const activeStep: StepKey | null =
status && !status.ollama_ready
? 'ollama_ready'
@@ -0,0 +1,119 @@
import { useState } from 'react';
import { SourcePicker } from './SourcePicker';
import { SourceConnectFlow } from './SourceConnectFlow';
import { IngestDashboard } from './IngestDashboard';
import { ReadyScreen } from './ReadyScreen';
import type { WizardStep } from '../../types/connectors';
// ---------------------------------------------------------------------------
// SetupWizard
// ---------------------------------------------------------------------------
export function SetupWizard({ onComplete }: { onComplete: (firstQuery?: string) => void }) {
const [step, setStep] = useState<WizardStep>('pick');
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const [connectedIds, setConnectedIds] = useState<string[]>([]);
const handlePick = (ids: string[]) => {
setSelectedIds(ids);
setStep('connect');
};
const handleConnectComplete = () => {
// connectedIds: those actually in selectedIds that were not skipped.
// We don't track skip state here — IngestDashboard receives all selected
// (skipped ones will just fail gracefully on sync status fetch).
setConnectedIds(selectedIds);
setStep('ingest');
};
const handleIngestReady = () => {
setStep('ready');
};
const handleStart = (query?: string) => {
onComplete(query);
};
return (
<div
className="fixed inset-0 flex items-center justify-center"
style={{ background: 'var(--color-bg)' }}
>
<div
className="w-full max-w-2xl mx-6 p-8 rounded-2xl"
style={{
background: 'var(--color-bg-secondary)',
border: '1px solid var(--color-border)',
maxHeight: '90vh',
display: 'flex',
flexDirection: 'column',
}}
>
{/* Step indicator */}
<div className="flex items-center gap-2 mb-8">
{(['pick', 'connect', 'ingest', 'ready'] as WizardStep[]).map((s, i) => (
<div key={s} className="flex items-center gap-2">
<div
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-bold transition-all"
style={{
background:
s === step
? 'var(--color-accent)'
: ['pick', 'connect', 'ingest', 'ready'].indexOf(s) <
['pick', 'connect', 'ingest', 'ready'].indexOf(step)
? 'var(--color-accent-subtle)'
: 'var(--color-bg-tertiary)',
color:
s === step
? 'white'
: ['pick', 'connect', 'ingest', 'ready'].indexOf(s) <
['pick', 'connect', 'ingest', 'ready'].indexOf(step)
? 'var(--color-accent)'
: 'var(--color-text-tertiary)',
}}
>
{i + 1}
</div>
{i < 3 && (
<div
className="w-8 h-0.5 rounded-full"
style={{
background:
['pick', 'connect', 'ingest', 'ready'].indexOf(s) <
['pick', 'connect', 'ingest', 'ready'].indexOf(step)
? 'var(--color-accent)'
: 'var(--color-border)',
}}
/>
)}
</div>
))}
</div>
{/* Step content */}
<div className="flex-1 overflow-hidden">
{step === 'pick' && <SourcePicker onContinue={handlePick} />}
{step === 'connect' && (
<SourceConnectFlow
selectedIds={selectedIds}
onComplete={handleConnectComplete}
/>
)}
{step === 'ingest' && (
<IngestDashboard
connectedIds={connectedIds}
onReady={handleIngestReady}
/>
)}
{step === 'ready' && (
<ReadyScreen
connectedSources={connectedIds}
onStart={handleStart}
/>
)}
</div>
</div>
</div>
);
}