(props: BootSplashProps)
| 44 | } |
| 45 | |
| 46 | export function BootSplash(props: BootSplashProps): React.ReactElement { |
| 47 | const { config, registry, router, onDone } = props; |
| 48 | const { stdout } = useStdout(); |
| 49 | const cols = stdout?.columns ?? 80; |
| 50 | const wide = cols >= LOGO_WIDTH + 4; |
| 51 | |
| 52 | const motion = !!stdout?.isTTY && process.env.QODEX_NO_SPLASH !== '1'; |
| 53 | |
| 54 | const steps = useMemo(() => { |
| 55 | let modelCount = 0; |
| 56 | try { modelCount = router.listAvailableModels().length; } catch { /* ignore */ } |
| 57 | return buildBootSteps({ |
| 58 | modelCount, |
| 59 | toolCount: registry.list().length, |
| 60 | model: config.defaults.model, |
| 61 | autoRetrieve: (config as any).context?.autoRetrieve !== false, |
| 62 | draftModel: (config.providers as any)?.openai?.draftModel ?? (config.providers as any)?.ollama?.draftModel, |
| 63 | }); |
| 64 | }, [config, registry, router]); |
| 65 | |
| 66 | // `revealed` = how many steps are fully online (✓). The step at index `revealed` |
| 67 | // (if any) is the one currently spinning. |
| 68 | const [revealed, setRevealed] = useState(0); |
| 69 | // Shimmer: slide the gradient phase continuously while the splash is up. |
| 70 | const phase = useShimmer(motion, SHIMMER_INTERVAL_MS); |
| 71 | |
| 72 | // No motion (piped / disabled) → don't animate, hand off on next tick. |
| 73 | useEffect(() => { |
| 74 | if (motion) return; |
| 75 | const t = setTimeout(onDone, 0); |
| 76 | return () => clearTimeout(t); |
| 77 | }, [motion, onDone]); |
| 78 | |
| 79 | // Reveal steps one at a time; when all are online, hold briefly then finish. |
| 80 | useEffect(() => { |
| 81 | if (!motion) return; |
| 82 | if (revealed >= steps.length) { |
| 83 | const done = setTimeout(onDone, HOLD_AFTER_DONE_MS); |
| 84 | return () => clearTimeout(done); |
| 85 | } |
| 86 | const next = setTimeout(() => setRevealed(r => r + 1), STEP_INTERVAL_MS); |
| 87 | return () => clearTimeout(next); |
| 88 | }, [motion, revealed, steps.length, onDone]); |
| 89 | |
| 90 | const progress = steps.length === 0 ? 1 : revealed / steps.length; |
| 91 | |
| 92 | return ( |
| 93 | <Box flexDirection="column" paddingX={2} paddingY={1}> |
| 94 | {/* Wordmark — full ANSI-shadow logo on wide terminals, compact mark otherwise. */} |
| 95 | {wide ? ( |
| 96 | <Box flexDirection="column"> |
| 97 | {LOGO.map((line, i) => ( |
| 98 | <GradientText key={i} text={line} stops={AURORA} phase={phase + i * 0.05} bold /> |
| 99 | ))} |
| 100 | </Box> |
| 101 | ) : ( |
| 102 | <GradientText text="✦ Q O D E X" stops={AURORA} phase={phase} bold /> |
| 103 | )} |
nothing calls this directly
no test coverage detected