"use client"; import { motion, useInView } from "motion/react"; import { useEffect, useMemo, useRef, useState } from "react"; import { useReducedMotion } from "@/app/hooks/useReducedMotion"; interface TextDiffuseRevealProps { text: string; className?: string; startDelay?: number; once?: boolean; amount?: number; } const EXPO_OUT = [0.16, 1, 0.3, 1] as const; export function TextDiffuseReveal({ text, className = "", startDelay = 0, once = true, amount = 0.3, }: Readonly) { const ref = useRef(null); const shouldReduceMotion = useReducedMotion(); const isInView = useInView(ref, { once, amount }); const [staggerDelay, setStaggerDelay] = useState(0.03); const [completedChars, setCompletedChars] = useState(0); useEffect(() => { const media = window.matchMedia("(max-width: 768px)"); const updateStagger = () => setStaggerDelay(media.matches ? 0.015 : 0.03); updateStagger(); media.addEventListener("change", updateStagger); return () => media.removeEventListener("change", updateStagger); }, []); const tokens = useMemo(() => { let offset = 0; return text .split(/(\n|\s+)/) .filter(Boolean) .map((value) => { const key = `${offset}:${value}`; offset += value.length; return { key, value }; }); }, [text]); const animatedChars = useMemo( () => Array.from(text).filter((char) => char !== "\n" && char !== " ").length, [text], ); useEffect(() => { if (!isInView) { setCompletedChars(0); } }, [isInView]); const hasActiveWillChange = !shouldReduceMotion && isInView && completedChars < animatedChars; let animatedIndex = 0; return ( {text} ); }