{"version":3,"file":"tokenFade.mjs","names":[],"sources":["../../../src/Highlighter/SyntaxHighlighter/tokenFade.ts"],"sourcesContent":["import { STREAM_FADE_DURATION } from '@lobehub/streamdown';\nimport type { CSSProperties } from 'react';\nimport type { ThemedToken } from 'shiki';\n\nconst MIN_GAP_MS = 16;\nconst MAX_GAP_MS = 160;\nconst MIN_CHAR_PACE_MS = 2;\nconst MAX_CHAR_PACE_MS = 18;\n\nexport interface TokenFadeStore {\n  /**\n   * Birth timestamp per source char. A char's birth is the birth of the\n   * token it is displayed in, and it only ever moves earlier: shiki\n   * re-splits the last line as it completes (`'rea` becomes `'`, `rea`,\n   * `'`) and merges tokens the other way, and a char that was already on\n   * screen at some opacity must never drop below it in the new token.\n   */\n  births: number[];\n  lastCommitTs: number;\n  styles: Map<number, { birth: number; style: CSSProperties | null }>;\n}\n\nexport const createTokenFadeStore = (): TokenFadeStore => ({\n  births: [],\n  lastCommitTs: 0,\n  styles: new Map(),\n});\n\nconst clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nconst RETOKENIZED_LINES = 2;\n\nexport const markTokenBirths = (\n  store: TokenFadeStore,\n  lines: ThemedToken[][],\n  now: number,\n): number[] => {\n  const lineStarts: number[] = [];\n  const tokens: { length: number; offset: number }[] = [];\n  let offset = 0;\n\n  for (const [lineIndex, line] of lines.entries()) {\n    if (lineIndex > 0) offset += 1;\n    lineStarts.push(offset);\n    if (lineIndex >= lines.length - RETOKENIZED_LINES) {\n      for (const token of line) {\n        tokens.push({ length: token.content.length, offset });\n        offset += token.content.length;\n      }\n    } else {\n      for (const token of line) offset += token.content.length;\n    }\n  }\n\n  const { births } = store;\n  const newChars = offset - births.length;\n  if (newChars > 0) {\n    const gapMs =\n      store.lastCommitTs === 0\n        ? MIN_GAP_MS\n        : clamp(now - store.lastCommitTs, MIN_GAP_MS, MAX_GAP_MS);\n    const pace = clamp(gapMs / newChars, MIN_CHAR_PACE_MS, MAX_CHAR_PACE_MS);\n    const cap = now + gapMs + STREAM_FADE_DURATION;\n    for (let i = births.length; i < offset; i++) {\n      const chained = i > 0 ? births[i - 1] + pace : now;\n      births.push(Math.min(cap, Math.max(chained, now)));\n    }\n    store.lastCommitTs = now;\n  }\n\n  for (const token of tokens) {\n    let birth = births[token.offset];\n    for (let i = token.offset + 1; i < token.offset + token.length; i++) {\n      if (births[i] < birth) birth = births[i];\n    }\n    for (let i = token.offset; i < token.offset + token.length; i++) births[i] = birth;\n  }\n\n  return lineStarts;\n};\n\nexport const resolveTokenFadeStyle = (\n  store: TokenFadeStore,\n  offset: number,\n  now: number,\n): CSSProperties | null => {\n  const birth = store.births[offset];\n  const cached = store.styles.get(offset);\n  if (cached && cached.birth === birth) return cached.style;\n\n  const elapsed = now - birth;\n  const style = elapsed >= STREAM_FADE_DURATION ? null : { animationDelay: `${-elapsed}ms` };\n  store.styles.set(offset, { birth, style });\n  return style;\n};\n"],"mappings":";;AAIA,MAAM,aAAa;AACnB,MAAM,aAAa;AACnB,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AAezB,MAAa,8BAA8C;CACzD,QAAQ,CAAC;CACT,cAAc;CACd,wBAAQ,IAAI,IAAI;AAClB;AAEA,MAAM,SAAS,OAAe,KAAa,QAAgB,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAE7F,MAAM,oBAAoB;AAE1B,MAAa,mBACX,OACA,OACA,QACa;CACb,MAAM,aAAuB,CAAC;CAC9B,MAAM,SAA+C,CAAC;CACtD,IAAI,SAAS;CAEb,KAAK,MAAM,CAAC,WAAW,SAAS,MAAM,QAAQ,GAAG;EAC/C,IAAI,YAAY,GAAG,UAAU;EAC7B,WAAW,KAAK,MAAM;EACtB,IAAI,aAAa,MAAM,SAAS,mBAC9B,KAAK,MAAM,SAAS,MAAM;GACxB,OAAO,KAAK;IAAE,QAAQ,MAAM,QAAQ;IAAQ;GAAO,CAAC;GACpD,UAAU,MAAM,QAAQ;EAC1B;OAEA,KAAK,MAAM,SAAS,MAAM,UAAU,MAAM,QAAQ;CAEtD;CAEA,MAAM,EAAE,WAAW;CACnB,MAAM,WAAW,SAAS,OAAO;CACjC,IAAI,WAAW,GAAG;EAChB,MAAM,QACJ,MAAM,iBAAiB,IACnB,aACA,MAAM,MAAM,MAAM,cAAc,YAAY,UAAU;EAC5D,MAAM,OAAO,MAAM,QAAQ,UAAU,kBAAkB,gBAAgB;EACvE,MAAM,MAAM,MAAM,QAAQ;EAC1B,KAAK,IAAI,IAAI,OAAO,QAAQ,IAAI,QAAQ,KAAK;GAC3C,MAAM,UAAU,IAAI,IAAI,OAAO,IAAI,KAAK,OAAO;GAC/C,OAAO,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI,SAAS,GAAG,CAAC,CAAC;EACnD;EACA,MAAM,eAAe;CACvB;CAEA,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,QAAQ,OAAO,MAAM;EACzB,KAAK,IAAI,IAAI,MAAM,SAAS,GAAG,IAAI,MAAM,SAAS,MAAM,QAAQ,KAC9D,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO;EAExC,KAAK,IAAI,IAAI,MAAM,QAAQ,IAAI,MAAM,SAAS,MAAM,QAAQ,KAAK,OAAO,KAAK;CAC/E;CAEA,OAAO;AACT;AAEA,MAAa,yBACX,OACA,QACA,QACyB;CACzB,MAAM,QAAQ,MAAM,OAAO;CAC3B,MAAM,SAAS,MAAM,OAAO,IAAI,MAAM;CACtC,IAAI,UAAU,OAAO,UAAU,OAAO,OAAO,OAAO;CAEpD,MAAM,UAAU,MAAM;CACtB,MAAM,QAAQ,WAAW,uBAAuB,OAAO,EAAE,gBAAgB,GAAG,CAAC,QAAQ,IAAI;CACzF,MAAM,OAAO,IAAI,QAAQ;EAAE;EAAO;CAAM,CAAC;CACzC,OAAO;AACT"}