diff --git a/web/src/components/architecture/message-flow.tsx b/web/src/components/architecture/message-flow.tsx index ff25fd55e..5a34c9a8f 100644 --- a/web/src/components/architecture/message-flow.tsx +++ b/web/src/components/architecture/message-flow.tsx @@ -1,7 +1,7 @@ "use client"; import { useState, useEffect, useRef } from "react"; -import { motion, AnimatePresence } from "framer-motion"; +import { motion } from "framer-motion"; const FLOW_STEPS = [ { role: "user", label: "user", color: "bg-blue-500" }, @@ -16,20 +16,22 @@ const FLOW_STEPS = [ export function MessageFlow() { const [count, setCount] = useState(0); - const intervalRef = useRef | null>(null); + const timeoutRef = useRef | null>(null); useEffect(() => { - intervalRef.current = setInterval(() => { - setCount((prev) => { - if (prev >= FLOW_STEPS.length) { - setTimeout(() => setCount(0), 1500); - return prev; - } - return prev + 1; - }); - }, 800); + // Single self-scheduling timer: only ever one timeout is pending, so a + // completed cycle can't queue multiple resets that snap the count back. + const step = (current: number) => { + const next = current >= FLOW_STEPS.length ? 0 : current + 1; + setCount(next); + // Hold on the full array before restarting the cycle. + const delay = next >= FLOW_STEPS.length ? 1500 : 800; + timeoutRef.current = setTimeout(() => step(next), delay); + }; + + timeoutRef.current = setTimeout(() => step(0), 800); return () => { - if (intervalRef.current) clearInterval(intervalRef.current); + if (timeoutRef.current) clearTimeout(timeoutRef.current); }; }, []); @@ -43,22 +45,21 @@ export function MessageFlow() { len={count} -
- - {FLOW_STEPS.slice(0, count).map((step, i) => ( - - - {step.label} - - - ))} - +
+ {FLOW_STEPS.slice(0, count).map((step, i) => ( + + + {step.label} + + + ))} {count === 0 && (
[]