{"version":3,"sources":["../../../src/utils/smooth/useSmooth.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useMessage } from \"../../context\";\nimport {\n  ContentPartStatus,\n  ReasoningContentPart,\n  TextContentPart,\n} from \"../../types/AssistantTypes\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\nimport { ContentPartState } from \"../../api/ContentPartRuntime\";\n\nclass TextStreamAnimator {\n  private animationFrameId: number | null = null;\n  private lastUpdateTime: number = Date.now();\n\n  public targetText: string = \"\";\n\n  constructor(\n    public currentText: string,\n    private setText: (newText: string) => void,\n  ) {}\n\n  start() {\n    if (this.animationFrameId !== null) return;\n    this.lastUpdateTime = Date.now();\n    this.animate();\n  }\n\n  stop() {\n    if (this.animationFrameId !== null) {\n      cancelAnimationFrame(this.animationFrameId);\n      this.animationFrameId = null;\n    }\n  }\n\n  private animate = () => {\n    const currentTime = Date.now();\n    const deltaTime = currentTime - this.lastUpdateTime;\n    let timeToConsume = deltaTime;\n\n    const remainingChars = this.targetText.length - this.currentText.length;\n    const baseTimePerChar = Math.min(5, 250 / remainingChars);\n\n    let charsToAdd = 0;\n    while (timeToConsume >= baseTimePerChar && charsToAdd < remainingChars) {\n      charsToAdd++;\n      timeToConsume -= baseTimePerChar;\n    }\n\n    if (charsToAdd !== remainingChars) {\n      this.animationFrameId = requestAnimationFrame(this.animate);\n    } else {\n      this.animationFrameId = null;\n    }\n    if (charsToAdd === 0) return;\n\n    this.currentText = this.targetText.slice(\n      0,\n      this.currentText.length + charsToAdd,\n    );\n    this.lastUpdateTime = currentTime - timeToConsume;\n    this.setText(this.currentText);\n  };\n}\n\nconst SMOOTH_STATUS: ContentPartStatus = Object.freeze({\n  type: \"running\",\n});\n\nexport const useSmooth = (\n  state: ContentPartState & (TextContentPart | ReasoningContentPart),\n  smooth: boolean = false,\n): ContentPartState & (TextContentPart | ReasoningContentPart) => {\n  const { text } = state;\n  const id = useMessage({\n    optional: true,\n    selector: (m: { id: string }) => m.id,\n  });\n\n  const idRef = useRef(id);\n  const [displayedText, setDisplayedText] = useState(text);\n\n  const smoothStatusStore = useSmoothStatusStore({ optional: true });\n  const setText = useCallbackRef((text: string) => {\n    setDisplayedText(text);\n    if (smoothStatusStore) {\n      const target =\n        displayedText !== text || state.status.type === \"running\"\n          ? SMOOTH_STATUS\n          : state.status;\n      writableStore(smoothStatusStore).setState(target, true);\n    }\n  });\n\n  // TODO this is hacky\n  useEffect(() => {\n    if (smoothStatusStore) {\n      const target =\n        displayedText !== text || state.status.type === \"running\"\n          ? SMOOTH_STATUS\n          : state.status;\n      writableStore(smoothStatusStore).setState(target, true);\n    }\n  }, [smoothStatusStore, text, displayedText, state.status]);\n\n  const [animatorRef] = useState<TextStreamAnimator>(\n    new TextStreamAnimator(text, setText),\n  );\n\n  useEffect(() => {\n    if (!smooth) {\n      animatorRef.stop();\n      return;\n    }\n\n    if (idRef.current !== id || !text.startsWith(animatorRef.targetText)) {\n      idRef.current = id;\n      setText(text);\n\n      animatorRef.currentText = text;\n      animatorRef.targetText = text;\n      animatorRef.stop();\n\n      return;\n    }\n\n    animatorRef.targetText = text;\n    animatorRef.start();\n  }, [setText, animatorRef, id, smooth, text]);\n\n  useEffect(() => {\n    return () => {\n      animatorRef.stop();\n    };\n  }, [animatorRef]);\n\n  return useMemo(\n    () =>\n      smooth\n        ? {\n            type: \"text\",\n            text: displayedText,\n            status: text === displayedText ? state.status : SMOOTH_STATUS,\n          }\n        : state,\n    [smooth, displayedText, state, text],\n  );\n};\n"],"mappings":"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","names":["text"]}