Files
letta-code/src/cli/components/AnimatedLogo.tsx
2026-01-29 16:51:21 -08:00

135 lines
3.4 KiB
TypeScript

import { Text } from "ink";
import { useEffect, useState } from "react";
import { colors } from "./colors";
function fixBunEncoding(text: string): string {
if (typeof Bun === "undefined") return text;
// Replace literal characters with Unicode codepoints
return text
.replace(/█/g, String.fromCharCode(0x2588)) // Full block
.replace(/▓/g, String.fromCharCode(0x2593)) // Dark shade
.replace(/▒/g, String.fromCharCode(0x2592)) // Medium shade
.replace(/░/g, String.fromCharCode(0x2591)); // Light shade
}
// Define animation frames - 3D rotation effect with gradient (█ → ▓ → ▒ → ░)
// Each frame is ~10 chars wide, 5 lines tall - matches login dialog asciiLogo size
const logoFrames = [
// 1. Front view (fully facing)
` ██████
██ ██
██ ██ ██
██ ██
██████ `,
// 2. Just starting to turn right
` ▓█████
▓█ ▓█
▓█ ▓█ ▓█
▓█ ▓█
▓█████ `,
// 3. Slight right turn
` ▓▓████
▓▓ ▓▓
▓▓ ▓▓ ▓▓
▓▓ ▓▓
▓▓████ `,
// 4. More right (gradient deepening)
` ░▓▓███
░▓▓ ░▓▓
░▓▓ ░▓ ░▓▓
░▓▓ ░▓▓
░▓▓███ `,
// 5. Even more right
` ░░▓▓██
░▓▓ ░▓▓
░▓▓░▓░▓▓
░▓▓ ░▓▓
░░▓▓██ `,
// 6. Approaching side
` ░▓▓█
░░▓░░▓
░░▓▓░▓
░░▓░░▓
░▓▓█ `,
// 7. Almost side
` ░▓▓▓
░▓░▓
░▓▓▓
░▓░▓
░▓▓▓ `,
// 8. Side view
` ▓▓▓▓
▓▓▓▓
▓▓▓▓
▓▓▓▓
▓▓▓▓ `,
// 9. Leaving side (mirror of 7)
` ▓▓▓░
▓░▓░
▓▓▓░
▓░▓░
▓▓▓░ `,
// 10. Past side (mirror of 6)
` █▓▓░
▓░░▓░░
▓░▓▓░░
▓░░▓░░
█▓▓░ `,
// 11. More past side (mirror of 5)
` ██▓▓░░
▓▓░ ▓▓░
▓▓░▓░▓▓░
▓▓░ ▓▓░
██▓▓░░ `,
// 12. Returning (mirror of 4)
` ███▓▓░
▓▓░ ▓▓░
▓▓░ ▓░ ▓▓░
▓▓░ ▓▓░
███▓▓░ `,
// 13. Almost front (mirror of 3)
` ████▓▓
▓▓ ▓▓
▓▓ ▓▓ ▓▓
▓▓ ▓▓
████▓▓ `,
// 14. Nearly front (mirror of 2)
` █████▓
█▓ █▓
█▓ █▓ █▓
█▓ █▓
█████▓ `,
].map(fixBunEncoding);
interface AnimatedLogoProps {
color?: string;
}
export function AnimatedLogo({
color = colors.welcome.accent,
}: AnimatedLogoProps) {
const [frame, setFrame] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setFrame((prev) => (prev + 1) % logoFrames.length);
}, 100);
return () => clearInterval(timer);
}, []);
const logoLines = logoFrames[frame]?.split("\n") ?? [];
return (
<>
{logoLines.map((line, idx) => (
// biome-ignore lint/suspicious/noArrayIndexKey: Logo lines are static and never reorder
<Text key={idx} bold color={color}>
{line}
</Text>
))}
</>
);
}