import { useEffect, useRef, useState } from "react"; import { motionEnabled } from "@/lib/motion"; const GLYPHS = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン0123456789▚▞▮◎"; /** * Cyberdeck decode: the label periodically dissolves into katakana and * terminal glyphs, then settles back left to right. Decorative labels only, * never live figures. */ export function Scramble({ text, interval = 9000, className, }: { text: string; interval?: number; className?: string; }) { const [display, setDisplay] = useState(text); const raf = useRef(0); useEffect(() => { if (!motionEnabled) { setDisplay(text); return; } let cancelled = false; const decode = () => { const start = performance.now(); const dur = 950; const tick = (t: number) => { if (cancelled) return; const p = Math.min((t - start) / dur, 1); const settled = Math.floor(p * text.length); let out = text.slice(0, settled); for (let i = settled; i < text.length; i++) { const ch = text[i]; out += ch === " " ? " " : GLYPHS[(Math.floor(t / 48) + i * 7) % GLYPHS.length]; } setDisplay(out); if (p < 1) raf.current = requestAnimationFrame(tick); else setDisplay(text); }; raf.current = requestAnimationFrame(tick); }; decode(); const jitter = (text.length * 137) % 3000; const timer = setInterval(decode, interval + jitter); return () => { cancelled = true; clearInterval(timer); cancelAnimationFrame(raf.current); }; }, [text, interval]); return {display}; }