{"version":3,"file":"useStreamQueue.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/useStreamQueue.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\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;\nconst FADE_DURATION = 280;\n\nfunction countChars(text: string): number {\n  return [...text].length;\n}\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) + 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":";;AASA,MAAM,aAAa;AACnB,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAC3B,MAAM,gBAAgB;AAEtB,SAAS,WAAW,MAAsB;AACxC,QAAO,CAAC,GAAG,KAAK,CAAC;;AAGnB,SAAS,iBAAiB,aAAqB,WAA2B;CAExE,IAAI,QAAQ,cADS,IAAI,cAAc;AAEvC,SAAQ,KAAK,IAAI,OAAO,qBAAqB,KAAK,IAAI,WAAW,EAAE,CAAC;AACpE,QAAO;;AAST,SAAgB,eAAe,QAA2C;CACxE,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CACrD,MAAM,WAAW,OAA6C,KAAK;CACnE,MAAM,mBAAmB,OAAO,EAAE;CAClC,MAAM,iBAAiB,OAAO,EAAE;AAQhC,KAAI,OAAO,WAAW,KAAK,iBAAiB,YAAY,EACtD,gBAAe,UAAU;AAE3B,KAAI,OAAO,SAAS,iBAAiB,WAAW,iBAAiB,UAAU,GAAG;EAC5E,MAAM,WAAW,iBAAiB,UAAU;AAC5C,iBAAe,UAAU,KAAK,IAAI,eAAe,SAAS,WAAW,EAAE;;AAEzE,kBAAiB,UAAU,OAAO;AAGlC,iBAAgB;AACd,MAAI,OAAO,WAAW,GAAG;AACvB,oBAAiB,EAAE;AACnB,kBAAe,UAAU;AACzB,OAAI,SAAS,SAAS;AACpB,iBAAa,SAAS,QAAQ;AAC9B,aAAS,UAAU;;;IAGtB,CAAC,OAAO,OAAO,CAAC;CAEnB,MAAM,yBAAyB,KAAK,IAAI,eAAe,eAAe,QAAQ;CAC9E,MAAM,YAAY,OAAO,SAAS;CAElC,MAAM,gBAAgB,aACnB,UAA8B;AAC7B,MAAI,QAAQ,uBAAwB,QAAO;AAC3C,MAAI,UAAU,0BAA0B,QAAQ,UAAW,QAAO;AAClE,MAAI,UAAU,0BAA0B,UAAU,UAAW,QAAO;AACpE,SAAO;IAET,CAAC,wBAAwB,UAAU,CACpC;CAED,MAAM,cAAc,KAAK,IAAI,GAAG,YAAY,yBAAyB,EAAE;CAEvE,MAAM,iBAAiB,yBAAyB,YAAY,yBAAyB;CACrF,MAAM,qBACJ,kBAAkB,IAAI,WAAW,OAAO,iBAAiB,WAAW,GAAG,GAAG;CAG5E,MAAM,cAAc,kBAAkB,IAAI,iBADnB,iBAAiB,KAAK,aAAa,yBAAyB,YAAY;CAE/F,MAAM,kBAAkB,eAAe,IAAI,WAAW,OAAO,cAAc,WAAW,GAAG,GAAG;CAG5F,MAAM,YAAY,OAAO;EAAE,OAAO;EAAY,OAAO;EAAI,CAAC;AAC1D,KAAI,eAAe,KAAK,gBAAgB,UAAU,QAAQ,MACxD,WAAU,UAAU;EAClB,OAAO,iBAAiB,aAAa,gBAAgB;EACrD,OAAO;EACR;CAEH,MAAM,YAAY,eAAe,IAAI,UAAU,QAAQ,QAAQ;CAE/D,MAAM,kBAAkB,kBAAkB;AACxC,mBAAiB,yBAAyB,EAAE;IAC3C,CAAC,uBAAuB,CAAC;AAE5B,iBAAgB;AACd,MAAI,SAAS,SAAS;AACpB,gBAAa,SAAS,QAAQ;AAC9B,YAAS,UAAU;;AAGrB,MAAI,iBAAiB,EAAG;EAExB,MAAM,YAAY,KAAK,IAAI,IAAI,qBAAqB,KAAK,UAAU,GAAG;AACtE,WAAS,UAAU,WAAW,iBAAiB,UAAU;AAEzD,eAAa;AACX,OAAI,SAAS,SAAS;AACpB,iBAAa,SAAS,QAAQ;AAC9B,aAAS,UAAU;;;IAGtB;EAAC;EAAgB;EAAoB;EAAW;EAAgB,CAAC;AAEpE,QAAO;EAAE;EAAW;EAAe;EAAa"}