Spaces:
Running
Running
File size: 2,393 Bytes
c0ddd13 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | import { useState, useEffect } from "react";
import { motion } from "motion/react";
import { Bot } from "lucide-react";
function useLoadingMessages() {
const [messages, setMessages] = useState<string[]>([]);
useEffect(() => {
fetch("/loading-messages.yaml")
.then((r) => r.text())
.then((text) => {
const parsed = text
.split("\n")
.filter((l) => l.trimStart().startsWith("- "))
.map((l) => l.replace(/^\s*- /, "").trim())
.filter(Boolean);
if (parsed.length > 0) setMessages(parsed);
})
.catch(() => {});
}, []);
return messages;
}
export default function TypingIndicator() {
const loadingMessages = useLoadingMessages();
const [phraseIndex, setPhraseIndex] = useState(0);
useEffect(() => {
if (loadingMessages.length === 0) return;
setPhraseIndex(Math.floor(Math.random() * loadingMessages.length));
const id = setInterval(() => {
setPhraseIndex((prev) => {
let next: number;
do {
next = Math.floor(Math.random() * loadingMessages.length);
} while (loadingMessages.length > 1 && next === prev);
return next;
});
}, 300);
return () => clearInterval(id);
}, [loadingMessages]);
return (
<motion.div
className="flex gap-3 px-4 py-2"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25, ease: "easeOut" }}
>
<div className="w-7 h-7 rounded-full bg-gradient-to-br from-brand-green-light to-brand-green flex-shrink-0 flex items-center justify-center shadow-sm">
<Bot className="h-4 w-4 text-white" />
</div>
<div className="bg-white border border-neutral-100 rounded-2xl rounded-tl-sm shadow-sm px-4 py-3 min-w-[6rem]">
{loadingMessages.length > 0 ? (
<span className="text-sm text-neutral-400 font-medium">
{loadingMessages[phraseIndex]}…
</span>
) : (
<div className="flex items-center gap-1.5 animate-pulse">
<span className="w-2 h-2 rounded-full bg-neutral-300" />
<span className="w-2 h-2 rounded-full bg-neutral-300" style={{ animationDelay: "150ms" }} />
<span className="w-2 h-2 rounded-full bg-neutral-300" style={{ animationDelay: "300ms" }} />
</div>
)}
</div>
</motion.div>
);
}
|