|
1 | 1 | import { Box, Text } from "ink"; |
2 | | -import { useEffect, useState } from "react"; |
3 | 2 |
|
4 | 3 | /** |
5 | | - * Animated pixel-C brand mark. Mirrors `web/public/favicon.svg`: |
| 4 | + * Static pixel-C brand mark. Mirrors `web/public/favicon.svg`: |
6 | 5 | * 5-row × 4-col grid, 9 filled pixels (3 top + 3 left + 3 bottom). |
7 | 6 | * Each SVG pixel renders as two block chars wide so the C reads |
8 | 7 | * proportionally in a 1:2 cell-ratio terminal. |
9 | | - * |
10 | | - * When `animate` is false, renders a static cyan C — used in the |
11 | | - * wizard header and OAuth-running screen as a brand mark. |
12 | | - * |
13 | | - * When `animate` is true, scans a "bright" row top → bottom through |
14 | | - * the C every cycle. Subtle but unmistakably alive — gives the agent |
15 | | - * a heartbeat during long thinking turns without burning attention. |
16 | 8 | */ |
17 | 9 |
|
18 | 10 | const FILL = "██"; |
19 | 11 | const GAP = " "; |
20 | 12 |
|
21 | 13 | interface PixelCProps { |
22 | | - animate?: boolean; |
23 | 14 | color?: string; |
24 | | - dimColor?: string; |
25 | | - intervalMs?: number; |
26 | 15 | } |
27 | 16 |
|
28 | | -interface RowSpec { |
29 | | - /** The textual content of the row. */ |
30 | | - text: string; |
31 | | - /** Which animation step (0..4) this row corresponds to. */ |
32 | | - step: number; |
33 | | -} |
34 | | - |
35 | | -const ROWS: readonly RowSpec[] = [ |
36 | | - { text: `${GAP}${FILL}${FILL}${FILL}`, step: 0 }, |
37 | | - { text: FILL, step: 1 }, |
38 | | - { text: FILL, step: 2 }, |
39 | | - { text: FILL, step: 3 }, |
40 | | - { text: `${GAP}${FILL}${FILL}${FILL}`, step: 4 }, |
| 17 | +const ROWS: readonly string[] = [ |
| 18 | + `${GAP}${FILL}${FILL}${FILL}`, |
| 19 | + FILL, |
| 20 | + FILL, |
| 21 | + FILL, |
| 22 | + `${GAP}${FILL}${FILL}${FILL}`, |
41 | 23 | ]; |
42 | | -const STEPS = ROWS.length; |
43 | | - |
44 | | -export function PixelC({ animate = false, color = "cyan", dimColor = "gray", intervalMs = 180 }: PixelCProps) { |
45 | | - const [activeStep, setActiveStep] = useState(0); |
46 | | - |
47 | | - useEffect(() => { |
48 | | - if (!animate) return; |
49 | | - const id = setInterval(() => setActiveStep((s) => (s + 1) % STEPS), intervalMs); |
50 | | - return () => clearInterval(id); |
51 | | - }, [animate, intervalMs]); |
52 | 24 |
|
| 25 | +export function PixelC({ color = "cyan" }: PixelCProps) { |
53 | 26 | return ( |
54 | 27 | <Box flexDirection="column"> |
55 | | - {ROWS.map((row) => { |
56 | | - const rowColor = !animate ? color : row.step === activeStep ? color : dimColor; |
57 | | - return ( |
58 | | - <Text key={`pixc-${row.step}`} bold color={rowColor}> |
59 | | - {row.text} |
60 | | - </Text> |
61 | | - ); |
62 | | - })} |
| 28 | + {ROWS.map((text, i) => ( |
| 29 | + <Text key={`pixc-${i}`} bold color={color}> |
| 30 | + {text} |
| 31 | + </Text> |
| 32 | + ))} |
63 | 33 | </Box> |
64 | 34 | ); |
65 | 35 | } |
0 commit comments