{"version":3,"file":"useStreamQueue.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/useStreamQueue.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { STREAM_FADE_DURATION } from './style';\nimport { countChars } from './useSmoothStreamContent';\n\nexport interface BlockInfo {\n  content: string;\n  startOffset: number;\n}\n\nexport type BlockState = 'revealed' | 'animating' | 'streaming' | 'queued';\n\nconst BASE_DELAY = 18;\nconst ACCELERATION_FACTOR = 0.3;\nconst MAX_BLOCK_DURATION = 3000;\n\nfunction computeCharDelay(queueLength: number, charCount: number): number {\n  const acceleration = 1 + queueLength * ACCELERATION_FACTOR;\n  let delay = BASE_DELAY / acceleration;\n  delay = Math.min(delay, MAX_BLOCK_DURATION / Math.max(charCount, 1));\n  return delay;\n}\n\nexport interface UseStreamQueueReturn {\n  charDelay: number;\n  getBlockState: (index: number) => BlockState;\n  queueLength: number;\n}\n\nexport function useStreamQueue(blocks: BlockInfo[]): UseStreamQueueReturn {\n  const [revealedCount, setRevealedCount] = useState(0);\n  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const prevBlocksLenRef = useRef(0);\n  const minRevealedRef = useRef(0);\n\n  // Synchronous auto-reveal during render.\n  // When blocks grow, the previous tail (streaming block) is instantly\n  // promoted to revealed — its chars are already visible via stream-mode\n  // animation. This runs during render (not in effect) so there is NO\n  // intermediate frame where the old streaming block enters 'animating'\n  // state and gets stagger plugins that would restart its animations.\n  if (blocks.length === 0 && prevBlocksLenRef.current !== 0) {\n    minRevealedRef.current = 0;\n  }\n  if (blocks.length > prevBlocksLenRef.current && prevBlocksLenRef.current > 0) {\n    const prevTail = prevBlocksLenRef.current - 1;\n    minRevealedRef.current = Math.max(minRevealedRef.current, prevTail + 1);\n  }\n  prevBlocksLenRef.current = blocks.length;\n\n  // State reset when stream restarts (blocks empty)\n  useEffect(() => {\n    if (blocks.length === 0) {\n      setRevealedCount(0);\n      minRevealedRef.current = 0;\n      if (timerRef.current) {\n        clearTimeout(timerRef.current);\n        timerRef.current = null;\n      }\n    }\n  }, [blocks.length]);\n\n  const effectiveRevealedCount = Math.max(revealedCount, minRevealedRef.current);\n  const tailIndex = blocks.length - 1;\n\n  const getBlockState = useCallback(\n    (index: number): BlockState => {\n      if (index < effectiveRevealedCount) return 'revealed';\n      if (index === effectiveRevealedCount && index < tailIndex) return 'animating';\n      if (index === effectiveRevealedCount && index === tailIndex) return 'streaming';\n      return 'queued';\n    },\n    [effectiveRevealedCount, tailIndex],\n  );\n\n  const queueLength = Math.max(0, tailIndex - effectiveRevealedCount - 1);\n\n  const animatingIndex = effectiveRevealedCount < tailIndex ? effectiveRevealedCount : -1;\n  const animatingCharCount =\n    animatingIndex >= 0 ? countChars(blocks[animatingIndex]?.content ?? '') : 0;\n\n  const streamingIndex = animatingIndex < 0 && tailIndex >= effectiveRevealedCount ? tailIndex : -1;\n  const activeIndex = animatingIndex >= 0 ? animatingIndex : streamingIndex;\n  const activeCharCount = activeIndex >= 0 ? countChars(blocks[activeIndex]?.content ?? '') : 0;\n\n  // Freeze charDelay when entering a new active block (animating or streaming)\n  const frozenRef = useRef({ delay: BASE_DELAY, index: -1 });\n  if (activeIndex >= 0 && activeIndex !== frozenRef.current.index) {\n    frozenRef.current = {\n      delay: computeCharDelay(queueLength, activeCharCount),\n      index: activeIndex,\n    };\n  }\n  const charDelay = activeIndex >= 0 ? frozenRef.current.delay : BASE_DELAY;\n\n  const onAnimationDone = useCallback(() => {\n    setRevealedCount(effectiveRevealedCount + 1);\n  }, [effectiveRevealedCount]);\n\n  useEffect(() => {\n    if (timerRef.current) {\n      clearTimeout(timerRef.current);\n      timerRef.current = null;\n    }\n\n    if (animatingIndex < 0) return;\n\n    const totalTime = Math.max(0, (animatingCharCount - 1) * charDelay) + STREAM_FADE_DURATION;\n    timerRef.current = setTimeout(onAnimationDone, totalTime);\n\n    return () => {\n      if (timerRef.current) {\n        clearTimeout(timerRef.current);\n        timerRef.current = null;\n      }\n    };\n  }, [animatingIndex, animatingCharCount, charDelay, onAnimationDone]);\n\n  return { charDelay, getBlockState, queueLength };\n}\n"],"mappings":";;;AAYA,MAAM,aAAa;AACnB,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAE3B,SAAS,iBAAiB,aAAqB,WAA2B;CACxE,MAAM,eAAe,IAAI,cAAc;CACvC,IAAI,QAAQ,aAAa;CACzB,QAAQ,KAAK,IAAI,OAAO,qBAAqB,KAAK,IAAI,WAAW,CAAC,CAAC;CACnE,OAAO;AACT;AAQA,SAAgB,eAAe,QAA2C;CACxE,MAAM,CAAC,eAAe,oBAAoB,SAAS,CAAC;CACpD,MAAM,WAAW,OAA6C,IAAI;CAClE,MAAM,mBAAmB,OAAO,CAAC;CACjC,MAAM,iBAAiB,OAAO,CAAC;CAQ/B,IAAI,OAAO,WAAW,KAAK,iBAAiB,YAAY,GACtD,eAAe,UAAU;CAE3B,IAAI,OAAO,SAAS,iBAAiB,WAAW,iBAAiB,UAAU,GAAG;EAC5E,MAAM,WAAW,iBAAiB,UAAU;EAC5C,eAAe,UAAU,KAAK,IAAI,eAAe,SAAS,WAAW,CAAC;CACxE;CACA,iBAAiB,UAAU,OAAO;CAGlC,gBAAgB;EACd,IAAI,OAAO,WAAW,GAAG;GACvB,iBAAiB,CAAC;GAClB,eAAe,UAAU;GACzB,IAAI,SAAS,SAAS;IACpB,aAAa,SAAS,OAAO;IAC7B,SAAS,UAAU;GACrB;EACF;CACF,GAAG,CAAC,OAAO,MAAM,CAAC;CAElB,MAAM,yBAAyB,KAAK,IAAI,eAAe,eAAe,OAAO;CAC7E,MAAM,YAAY,OAAO,SAAS;CAElC,MAAM,gBAAgB,aACnB,UAA8B;EAC7B,IAAI,QAAQ,wBAAwB,OAAO;EAC3C,IAAI,UAAU,0BAA0B,QAAQ,WAAW,OAAO;EAClE,IAAI,UAAU,0BAA0B,UAAU,WAAW,OAAO;EACpE,OAAO;CACT,GACA,CAAC,wBAAwB,SAAS,CACpC;CAEA,MAAM,cAAc,KAAK,IAAI,GAAG,YAAY,yBAAyB,CAAC;CAEtE,MAAM,iBAAiB,yBAAyB,YAAY,yBAAyB;CACrF,MAAM,qBACJ,kBAAkB,IAAI,WAAW,OAAO,eAAe,EAAE,WAAW,EAAE,IAAI;CAG5E,MAAM,cAAc,kBAAkB,IAAI,iBADnB,iBAAiB,KAAK,aAAa,yBAAyB,YAAY;CAE/F,MAAM,kBAAkB,eAAe,IAAI,WAAW,OAAO,YAAY,EAAE,WAAW,EAAE,IAAI;CAG5F,MAAM,YAAY,OAAO;EAAE,OAAO;EAAY,OAAO;CAAG,CAAC;CACzD,IAAI,eAAe,KAAK,gBAAgB,UAAU,QAAQ,OACxD,UAAU,UAAU;EAClB,OAAO,iBAAiB,aAAa,eAAe;EACpD,OAAO;CACT;CAEF,MAAM,YAAY,eAAe,IAAI,UAAU,QAAQ,QAAQ;CAE/D,MAAM,kBAAkB,kBAAkB;EACxC,iBAAiB,yBAAyB,CAAC;CAC7C,GAAG,CAAC,sBAAsB,CAAC;CAE3B,gBAAgB;EACd,IAAI,SAAS,SAAS;GACpB,aAAa,SAAS,OAAO;GAC7B,SAAS,UAAU;EACrB;EAEA,IAAI,iBAAiB,GAAG;EAExB,MAAM,YAAY,KAAK,IAAI,IAAI,qBAAqB,KAAK,SAAS,IAAA;EAClE,SAAS,UAAU,WAAW,iBAAiB,SAAS;EAExD,aAAa;GACX,IAAI,SAAS,SAAS;IACpB,aAAa,SAAS,OAAO;IAC7B,SAAS,UAAU;GACrB;EACF;CACF,GAAG;EAAC;EAAgB;EAAoB;EAAW;CAAe,CAAC;CAEnE,OAAO;EAAE;EAAW;EAAe;CAAY;AACjD"}