UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

91 lines (89 loc) 3.28 kB
import { useCallback, useEffect, useRef, useState } from "react"; //#region src/Markdown/SyntaxMarkdown/useStreamQueue.ts const BASE_DELAY = 18; const ACCELERATION_FACTOR = .3; const MAX_BLOCK_DURATION = 3e3; const FADE_DURATION = 280; function countChars(text) { return [...text].length; } function computeCharDelay(queueLength, charCount) { let delay = BASE_DELAY / (1 + queueLength * ACCELERATION_FACTOR); delay = Math.min(delay, MAX_BLOCK_DURATION / Math.max(charCount, 1)); return delay; } function useStreamQueue(blocks) { const [revealedCount, setRevealedCount] = useState(0); const timerRef = useRef(null); const prevBlocksLenRef = useRef(0); const minRevealedRef = useRef(0); if (blocks.length === 0 && prevBlocksLenRef.current !== 0) minRevealedRef.current = 0; if (blocks.length > prevBlocksLenRef.current && prevBlocksLenRef.current > 0) { const prevTail = prevBlocksLenRef.current - 1; minRevealedRef.current = Math.max(minRevealedRef.current, prevTail + 1); } prevBlocksLenRef.current = blocks.length; useEffect(() => { if (blocks.length === 0) { setRevealedCount(0); minRevealedRef.current = 0; if (timerRef.current) { clearTimeout(timerRef.current); timerRef.current = null; } } }, [blocks.length]); const effectiveRevealedCount = Math.max(revealedCount, minRevealedRef.current); const tailIndex = blocks.length - 1; const getBlockState = useCallback((index) => { if (index < effectiveRevealedCount) return "revealed"; if (index === effectiveRevealedCount && index < tailIndex) return "animating"; if (index === effectiveRevealedCount && index === tailIndex) return "streaming"; return "queued"; }, [effectiveRevealedCount, tailIndex]); const queueLength = Math.max(0, tailIndex - effectiveRevealedCount - 1); const animatingIndex = effectiveRevealedCount < tailIndex ? effectiveRevealedCount : -1; const animatingCharCount = animatingIndex >= 0 ? countChars(blocks[animatingIndex]?.content ?? "") : 0; const activeIndex = animatingIndex >= 0 ? animatingIndex : animatingIndex < 0 && tailIndex >= effectiveRevealedCount ? tailIndex : -1; const activeCharCount = activeIndex >= 0 ? countChars(blocks[activeIndex]?.content ?? "") : 0; const frozenRef = useRef({ delay: BASE_DELAY, index: -1 }); if (activeIndex >= 0 && activeIndex !== frozenRef.current.index) frozenRef.current = { delay: computeCharDelay(queueLength, activeCharCount), index: activeIndex }; const charDelay = activeIndex >= 0 ? frozenRef.current.delay : BASE_DELAY; const onAnimationDone = useCallback(() => { setRevealedCount(effectiveRevealedCount + 1); }, [effectiveRevealedCount]); useEffect(() => { if (timerRef.current) { clearTimeout(timerRef.current); timerRef.current = null; } if (animatingIndex < 0) return; const totalTime = Math.max(0, (animatingCharCount - 1) * charDelay) + FADE_DURATION; timerRef.current = setTimeout(onAnimationDone, totalTime); return () => { if (timerRef.current) { clearTimeout(timerRef.current); timerRef.current = null; } }; }, [ animatingIndex, animatingCharCount, charDelay, onAnimationDone ]); return { charDelay, getBlockState, queueLength }; } //#endregion export { useStreamQueue }; //# sourceMappingURL=useStreamQueue.mjs.map