"use client"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; import { useEffect, useLayoutEffect, useRef, useState } from "react"; import { cn } from "@/lib/utils"; export interface StatusPillProps { phases?: string[]; doneLabel?: string; interval?: number; done?: boolean; loop?: boolean; size?: "sm" | "default"; className?: string; } const spring = { type: "spring", stiffness: 420, damping: 34 } as const; const easeOut = [0.23, 1, 0.32, 1] as const; function useMountedReducedMotion() { const prefers = useReducedMotion(); const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); return mounted && !!prefers; } export function StatusPill({ phases = ["Reading", "Searching", "Writing"], doneLabel = "Done", interval = 1400, done: doneProp, loop = false, size = "default", className, }: StatusPillProps) { const reduce = useMountedReducedMotion(); const [step, setStep] = useState(0); const controlled = doneProp !== undefined; const done = controlled ? doneProp : step >= phases.length; useEffect(() => { if (controlled) { if (doneProp) return; const t = setTimeout(() => setStep((s) => (s + 1) % phases.length), interval); return () => clearTimeout(t); } if (step >= phases.length) { if (!loop) return; const t = setTimeout(() => setStep(0), interval * 1.8); return () => clearTimeout(t); } const t = setTimeout(() => setStep((s) => s + 1), interval); return () => clearTimeout(t); }, [controlled, doneProp, step, phases.length, interval, loop]); const label = done ? doneLabel : (phases[step % phases.length] ?? ""); const text = done ? label : `${label}…`; const measureRef = useRef(null); const [width, setWidth] = useState(); useLayoutEffect(() => { if (measureRef.current) setWidth(measureRef.current.offsetWidth); }, [text]); return (
{text} {done ? {text} : {text}}
); } function ShimmerText({ children }: { children: string }) { const reduce = useMountedReducedMotion(); return ( {children} ); } export function StatusSpinner({ done = false, size = 18 }: { done?: boolean; size?: number }) { const reduce = useMountedReducedMotion(); return ( {done ? ( ) : ( )} ); }