{"version":3,"file":"rehypeStreamAnimated.mjs","names":[],"sources":["../../../src/Markdown/plugins/rehypeStreamAnimated.ts"],"sourcesContent":["import { type Element, type ElementContent, type Root } from 'hast';\nimport { type BuildVisitor } from 'unist-util-visit';\nimport { visit } from 'unist-util-visit';\n\nexport interface StreamAnimatedOptions {\n  baseCharCount?: number;\n  charDelay?: number;\n  fadeDuration?: number;\n  revealed?: boolean;\n  timelineElapsedMs?: number;\n}\n\nconst BLOCK_TAGS = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li']);\nconst SKIP_TAGS = new Set(['pre', 'code', 'table', 'svg']);\n\nfunction hasClass(node: Element, cls: string): boolean {\n  const cn = node.properties?.className;\n  if (Array.isArray(cn)) return cn.some((c) => String(c).includes(cls));\n  if (typeof cn === 'string') return cn.includes(cls);\n  return false;\n}\n\nexport const rehypeStreamAnimated = (options: StreamAnimatedOptions = {}) => {\n  const {\n    charDelay = 20,\n    fadeDuration = 150,\n    baseCharCount = 0,\n    revealed = false,\n    timelineElapsedMs,\n  } = options;\n  const hasTimeline = typeof timelineElapsedMs === 'number' && Number.isFinite(timelineElapsedMs);\n\n  return (tree: Root) => {\n    let globalCharIndex = 0;\n\n    const shouldSkip = (node: Element): boolean => {\n      return SKIP_TAGS.has(node.tagName) || hasClass(node, 'katex');\n    };\n\n    const wrapText = (node: Element) => {\n      const newChildren: ElementContent[] = [];\n      for (const child of node.children) {\n        if (child.type === 'text') {\n          for (const char of child.value) {\n            const relativeIndex = globalCharIndex - baseCharCount;\n            let className = 'stream-char';\n            let delay: number | undefined;\n\n            if (revealed) {\n              className = 'stream-char stream-char-revealed';\n            } else if (hasTimeline) {\n              const progress = (timelineElapsedMs as number) - globalCharIndex * charDelay;\n              if (progress >= fadeDuration) {\n                className = 'stream-char stream-char-revealed';\n              } else {\n                // Positive delay means \"not started yet\", negative keeps\n                // the current in-flight progress on rerender.\n                delay = -progress;\n              }\n            } else if (relativeIndex >= 0) {\n              // Newly appended chars start with staggered positive delay.\n              delay = relativeIndex * charDelay;\n            } else {\n              // Previously started chars continue fading with negative delay\n              // instead of being immediately switched to revealed.\n              const elapsed = -relativeIndex * charDelay;\n              if (elapsed >= fadeDuration) {\n                className = 'stream-char stream-char-revealed';\n              } else {\n                delay = -elapsed;\n              }\n            }\n\n            const properties: Record<string, any> = { className };\n            if (delay !== undefined && delay !== 0) {\n              properties.style = `animation-delay:${delay}ms`;\n            }\n            newChildren.push({\n              children: [{ type: 'text', value: char }],\n              properties,\n              tagName: 'span',\n              type: 'element',\n            });\n            globalCharIndex++;\n          }\n        } else if (child.type === 'element') {\n          if (!shouldSkip(child)) {\n            wrapText(child);\n          }\n          newChildren.push(child);\n        } else {\n          newChildren.push(child);\n        }\n      }\n      node.children = newChildren;\n    };\n\n    visit(tree, 'element', ((node: Element) => {\n      if (shouldSkip(node)) return 'skip';\n      if (BLOCK_TAGS.has(node.tagName)) {\n        wrapText(node);\n        return 'skip';\n      }\n    }) as BuildVisitor<Root, 'element'>);\n  };\n};\n"],"mappings":";;AAYA,MAAM,aAAa,IAAI,IAAI;CAAC;CAAK;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAK,CAAC;AAC3E,MAAM,YAAY,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAS;CAAM,CAAC;AAE1D,SAAS,SAAS,MAAe,KAAsB;CACrD,MAAM,KAAK,KAAK,YAAY;AAC5B,KAAI,MAAM,QAAQ,GAAG,CAAE,QAAO,GAAG,MAAM,MAAM,OAAO,EAAE,CAAC,SAAS,IAAI,CAAC;AACrE,KAAI,OAAO,OAAO,SAAU,QAAO,GAAG,SAAS,IAAI;AACnD,QAAO;;AAGT,MAAa,wBAAwB,UAAiC,EAAE,KAAK;CAC3E,MAAM,EACJ,YAAY,IACZ,eAAe,KACf,gBAAgB,GAChB,WAAW,OACX,sBACE;CACJ,MAAM,cAAc,OAAO,sBAAsB,YAAY,OAAO,SAAS,kBAAkB;AAE/F,SAAQ,SAAe;EACrB,IAAI,kBAAkB;EAEtB,MAAM,cAAc,SAA2B;AAC7C,UAAO,UAAU,IAAI,KAAK,QAAQ,IAAI,SAAS,MAAM,QAAQ;;EAG/D,MAAM,YAAY,SAAkB;GAClC,MAAM,cAAgC,EAAE;AACxC,QAAK,MAAM,SAAS,KAAK,SACvB,KAAI,MAAM,SAAS,OACjB,MAAK,MAAM,QAAQ,MAAM,OAAO;IAC9B,MAAM,gBAAgB,kBAAkB;IACxC,IAAI,YAAY;IAChB,IAAI;AAEJ,QAAI,SACF,aAAY;aACH,aAAa;KACtB,MAAM,WAAY,oBAA+B,kBAAkB;AACnE,SAAI,YAAY,aACd,aAAY;SAIZ,SAAQ,CAAC;eAEF,iBAAiB,EAE1B,SAAQ,gBAAgB;SACnB;KAGL,MAAM,UAAU,CAAC,gBAAgB;AACjC,SAAI,WAAW,aACb,aAAY;SAEZ,SAAQ,CAAC;;IAIb,MAAM,aAAkC,EAAE,WAAW;AACrD,QAAI,UAAU,KAAA,KAAa,UAAU,EACnC,YAAW,QAAQ,mBAAmB,MAAM;AAE9C,gBAAY,KAAK;KACf,UAAU,CAAC;MAAE,MAAM;MAAQ,OAAO;MAAM,CAAC;KACzC;KACA,SAAS;KACT,MAAM;KACP,CAAC;AACF;;YAEO,MAAM,SAAS,WAAW;AACnC,QAAI,CAAC,WAAW,MAAM,CACpB,UAAS,MAAM;AAEjB,gBAAY,KAAK,MAAM;SAEvB,aAAY,KAAK,MAAM;AAG3B,QAAK,WAAW;;AAGlB,QAAM,MAAM,aAAa,SAAkB;AACzC,OAAI,WAAW,KAAK,CAAE,QAAO;AAC7B,OAAI,WAAW,IAAI,KAAK,QAAQ,EAAE;AAChC,aAAS,KAAK;AACd,WAAO;;KAEyB"}