feat: logo spin (#597)

This commit is contained in:
jnjpng
2026-01-19 17:26:10 -08:00
committed by GitHub
parent 200f26250a
commit de174be822
3 changed files with 130 additions and 11 deletions

View File

@@ -5,7 +5,7 @@
import { hostname } from "node:os";
import { Box, Text, useApp, useInput } from "ink";
import { useState } from "react";
import { asciiLogo } from "../cli/components/AsciiArt.ts";
import { AnimatedLogo } from "../cli/components/AnimatedLogo";
import { colors } from "../cli/components/colors";
import { settingsManager } from "../settings-manager";
import { pollForToken, requestDeviceCode } from "./oauth";
@@ -139,7 +139,8 @@ export function SetupUI({ onComplete }: SetupUIProps) {
if (mode === "device-code") {
return (
<Box flexDirection="column" padding={1}>
<Text color={colors.welcome.accent}>{asciiLogo}</Text>
<AnimatedLogo color={colors.welcome.accent} />
<Text> </Text>
<Text bold>Login to the Letta Developer Platform</Text>
<Text> </Text>
<Text dimColor>Opening browser for authorization...</Text>
@@ -160,7 +161,8 @@ export function SetupUI({ onComplete }: SetupUIProps) {
// Main menu
return (
<Box flexDirection="column" padding={1}>
<Text color={colors.welcome.accent}>{asciiLogo}</Text>
<AnimatedLogo color={colors.welcome.accent} />
<Text> </Text>
<Text bold>Welcome to Letta Code!</Text>
<Text> </Text>
<Text>Let's get you authenticated:</Text>

View File

@@ -0,0 +1,123 @@
import { Text } from "ink";
import { useEffect, useState } from "react";
import { colors } from "./colors";
// 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)
` █████▓
█▓ █▓
█▓ █▓ █▓
█▓ █▓
█████▓ `,
];
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>
))}
</>
);
}

View File

@@ -8,7 +8,7 @@ import { getModelDisplayName } from "../../agent/model";
import { settingsManager } from "../../settings-manager";
import { getVersion } from "../../version";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { asciiLogo } from "./AsciiArt";
import { AnimatedLogo } from "./AnimatedLogo";
import { colors } from "./colors";
/**
@@ -80,7 +80,6 @@ export function WelcomeScreen({
const cwd = process.cwd();
const version = getVersion();
const logoLines = asciiLogo.trim().split("\n");
const tildePath = toTildePath(cwd);
// Get model display name (pretty name if available, otherwise last part of handle)
@@ -117,12 +116,7 @@ export function WelcomeScreen({
<Box flexDirection="row" marginTop={1}>
{/* Left column: Logo */}
<Box flexDirection="column" paddingLeft={1} paddingRight={2}>
{logoLines.map((line, idx) => (
// biome-ignore lint/suspicious/noArrayIndexKey: Logo lines are static and never reorder
<Text key={idx} bold color={colors.welcome.accent}>
{idx === 0 ? ` ${line}` : line}
</Text>
))}
<AnimatedLogo color={colors.welcome.accent} />
</Box>
{/* Right column: Text info */}