import type { Theme } from "@earendil-works/pi-coding-agent"; import { type Component, type TUI, truncateToWidth, } from "@earendil-works/pi-tui"; const AWAKE = [" /\\_/\\", " ( o.o ) working", " > ^ <"]; const BLINK = [" /\\_/\\", " ( -.- ) working", " > ^ <"]; const NAPPING = [" /\\_/\\", " ( -.- ) zzz", " > ^ <"]; export type AfkWidget = Component & { invalidate(): void; dispose(): void }; export function createAfkWidget( tui: TUI, theme: Theme, isNapping: () => boolean, ): AfkWidget { let blinking = false; let disposed = false; let timer: ReturnType | undefined; const syncAnimation = (): void => { if (disposed || isNapping()) { clearTimeout(timer); timer = undefined; return; } if (timer !== undefined) return; // Only the blink transitions change pixels; naps need no timer. timer = setTimeout( () => { timer = undefined; if (!isNapping()) blinking = !blinking; tui.requestRender(); syncAnimation(); }, blinking ? 700 : 2_100, ); timer.unref?.(); }; syncAnimation(); return { render(width: number): string[] { const art = isNapping() ? NAPPING : blinking ? BLINK : AWAKE; return art.map((line) => truncateToWidth(theme.fg("accent", line), width), ); }, invalidate(): void { syncAnimation(); tui.requestRender(); }, dispose(): void { if (disposed) return; disposed = true; syncAnimation(); }, }; }