import { useEffect, useRef, useState } from 'react' import { nextTypeDelay, nextTypeStep } from './studio-command-typing' export function useStudioCommandPanelAnimation(latestAssistantText: string) { const [animatedAssistantText, setAnimatedAssistantText] = useState('') const streamRateRef = useRef(0) const latestTextMetaRef = useRef<{ text: string; at: number }>({ text: '', at: 0 }) useEffect(() => { if (!latestAssistantText) { streamRateRef.current = 0 latestTextMetaRef.current = { text: '', at: 0 } const frame = window.requestAnimationFrame(() => { setAnimatedAssistantText('') }) return () => window.cancelAnimationFrame(frame) } const now = Date.now() const prev = latestTextMetaRef.current if (prev.text && latestAssistantText.startsWith(prev.text) && latestAssistantText.length > prev.text.length) { const deltaChars = latestAssistantText.length - prev.text.length const deltaMs = Math.max(1, now - prev.at) const charsPerSecond = (deltaChars * 1000) / deltaMs streamRateRef.current = streamRateRef.current === 0 ? charsPerSecond : streamRateRef.current * 0.68 + charsPerSecond * 0.32 } else if (!prev.text) { streamRateRef.current = 0 } latestTextMetaRef.current = { text: latestAssistantText, at: now } const frame = window.requestAnimationFrame(() => { setAnimatedAssistantText((current) => { if (!latestAssistantText.startsWith(current)) { return latestAssistantText.slice(0, 1) } return current || latestAssistantText.slice(0, 1) }) }) return () => window.cancelAnimationFrame(frame) }, [latestAssistantText]) useEffect(() => { if (!latestAssistantText) { return } if (animatedAssistantText === latestAssistantText) { return } const timer = window.setTimeout(() => { setAnimatedAssistantText((current) => { if (!latestAssistantText.startsWith(current)) { return latestAssistantText.slice(0, 1) } const nextLength = current.length + nextTypeStep(latestAssistantText.length - current.length) return latestAssistantText.slice(0, nextLength) }) }, nextTypeDelay(latestAssistantText, animatedAssistantText.length, streamRateRef.current)) return () => window.clearTimeout(timer) }, [animatedAssistantText, latestAssistantText]) return animatedAssistantText }