capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
113 lines • 6.7 kB
JavaScript
import React, { useState, useEffect } from 'react';
import { Box, Text } from 'ink';
import gradient from 'gradient-string';
export const StartupAnimation = ({ onComplete }) => {
const [stage, setStage] = useState('particles');
const [frame, setFrame] = useState(0);
const [typewriterText, setTypewriterText] = useState('');
const [showLogo, setShowLogo] = useState(false);
const [logoOpacity, setLogoOpacity] = useState(0);
const [taglineVisible, setTaglineVisible] = useState(false);
const [loadingDots, setLoadingDots] = useState('');
const capsuleLetters = ['C', 'A', 'P', 'S', 'U', 'L', 'E'];
const totalFrames = 80;
const particles = ['✦', '✧', '✨', '⬢', '◆', '●', '○', '⬡'];
useEffect(() => {
const timer = setInterval(() => {
setFrame(prev => {
const next = prev + 1;
if (next === 12) {
setStage('typewriter');
}
if (next >= 12 && next < 12 + capsuleLetters.length * 3) {
const index = Math.floor((next - 12) / 3);
setTypewriterText(capsuleLetters.slice(0, index + 1).join(''));
}
if (next === 30) {
setStage('logo');
setShowLogo(true);
}
if (next >= 30 && next <= 40) {
setLogoOpacity((next - 30) / 10);
}
if (next === 45) {
setTaglineVisible(true);
}
if (next >= 50) {
const dotCount = ((next - 50) % 4);
setLoadingDots('.'.repeat(dotCount));
}
if (next >= totalFrames) {
clearInterval(timer);
setTimeout(onComplete, 500);
}
return next;
});
}, 50);
return () => clearInterval(timer);
}, [onComplete]);
const renderParticles = () => {
if (stage === 'particles' && frame < 12) {
const particleElements = [];
const centerX = 30;
const centerY = 5;
for (let i = 0; i < 12; i++) {
const angle = (i / 12) * Math.PI * 2;
const delay = i * 0.5;
if (frame > delay) {
const particle = particles[i % particles.length];
const progress = Math.min((frame - delay) / 8, 1);
const radius = progress * 15;
const x = Math.floor(centerX + Math.cos(angle) * radius);
const y = Math.floor(centerY + Math.sin(angle) * radius * 0.5);
const opacity = 1 - progress * 0.5;
particleElements.push(React.createElement(Box, { key: i, position: "absolute", marginLeft: x, marginTop: y },
React.createElement(Text, { color: i % 3 === 0 ? 'cyan' : i % 3 === 1 ? 'magenta' : 'yellow', dimColor: opacity < 0.7 }, particle)));
}
}
return particleElements;
}
return null;
};
const logo = `
██████╗ █████╗ ██████╗ ███████╗██╗ ██╗██╗ ███████╗
██╔════╝██╔══██╗██╔══██╗██╔════╝██║ ██║██║ ██╔════╝
██║ ███████║██████╔╝███████╗██║ ██║██║ █████╗
██║ ██╔══██║██╔═══╝ ╚════██║██║ ██║██║ ██╔══╝
╚██████╗██║ ██║██║ ███████║╚██████╔╝███████╗███████╗
╚═════╝╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═════╝ ╚══════╝╚══════╝`;
const animatedLogo = showLogo ?
gradient(['#39ff14', '#00ff41', '#0fa']).multiline(logo) : '';
const tagline = "AI-Powered Software Engineering";
return (React.createElement(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: 20 },
React.createElement(Box, { marginBottom: 2 },
React.createElement(Text, { dimColor: true, italic: true }, "Press any key to skip...")),
stage === 'particles' && (React.createElement(Box, { position: "relative", width: 60, height: 12 }, renderParticles())),
stage === 'typewriter' && typewriterText && (React.createElement(Box, null,
React.createElement(Text, { bold: true },
typewriterText.split('').map((char, i) => (React.createElement(Text, { key: i, color: gradient(['#39ff14', '#00ff41', '#0fa']).multiline(char) }, char))),
React.createElement(Text, { color: "gray" }, '_'.repeat(7 - typewriterText.length))))),
showLogo && (React.createElement(Box, { flexDirection: "column", alignItems: "center" },
React.createElement(Text, { dimColor: logoOpacity < 0.7 }, animatedLogo))),
taglineVisible && (React.createElement(Box, { marginTop: 1 },
React.createElement(Text, { color: "cyan", bold: true }, gradient(['#0fa', '#39ff14']).multiline(tagline)))),
frame >= 50 && (React.createElement(Box, { marginTop: 2 },
React.createElement(Text, { color: "gray" },
"Initializing AI systems",
loadingDots))),
frame >= 35 && frame < totalFrames - 5 && (React.createElement(Box, { marginTop: 2, flexDirection: "column", alignItems: "center" },
React.createElement(Box, null,
React.createElement(Text, { color: "gray" }, "["),
Array.from({ length: 36 }).map((_, i) => {
const progress = (frame - 35) / (totalFrames - 40);
const filled = i < Math.floor(progress * 36);
const isHead = i === Math.floor(progress * 36) - 1;
return (React.createElement(Text, { key: i, color: isHead ? 'yellow' : filled ? 'green' : 'gray' }, isHead ? '▶' : filled ? '=' : '-'));
}),
React.createElement(Text, { color: "gray" }, "]")),
React.createElement(Box, { marginTop: 1 },
React.createElement(Text, { color: "gray", dimColor: true },
Math.floor(((frame - 35) / (totalFrames - 40)) * 100),
"%"))))));
};
//# sourceMappingURL=StartupAnimation.js.map