UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

106 lines (105 loc) 3.45 kB
"use client"; import { useStreamHighlight } from "../../hooks/useStreamHighlight.mjs"; import { createTokenFadeStore, markTokenBirths, resolveTokenFadeStyle } from "./tokenFade.mjs"; import { memo, useRef } from "react"; import { jsx } from "react/jsx-runtime"; import { cx } from "antd-style"; import { getTokenStyleObject } from "@shikijs/core"; //#region src/Highlighter/SyntaxHighlighter/StreamRenderer.tsx const normalizeStyleKeys = (style) => { const normalized = {}; Object.entries(style).forEach(([key, value]) => { const normalizedKey = key.replaceAll(/-([a-z])/g, (_, char) => char.toUpperCase()); normalized[normalizedKey] = value; }); return normalized; }; const getTokenInlineStyle = (token) => { const rawStyle = token.htmlStyle || getTokenStyleObject(token); return { ...normalizeStyleKeys(rawStyle), whiteSpace: "pre" }; }; const TokenSpan = memo(({ fadeStyle, token }) => { const style = fadeStyle ? { ...getTokenInlineStyle(token), ...fadeStyle } : getTokenInlineStyle(token); return /* @__PURE__ */ jsx("span", { className: fadeStyle ? "stream-char" : void 0, style, children: token.content }); }, (prev, next) => prev.token === next.token && prev.fadeStyle === next.fadeStyle); const TokenLine = memo(({ fade, line, now, start }) => { if (!line.length) return /* @__PURE__ */ jsx("span", { className: "line", children: /* @__PURE__ */ jsx("span", { style: { whiteSpace: "pre" }, children: "\xA0" }) }); let offset = start; return /* @__PURE__ */ jsx("span", { className: "line", children: line.map((token) => { const tokenOffset = offset; offset += token.content.length; return /* @__PURE__ */ jsx(TokenSpan, { fadeStyle: resolveTokenFadeStyle(fade, tokenOffset, now), token }, tokenOffset); }) }); }, (prev, next) => prev.line === next.line && prev.start === next.start); const StreamRenderer = memo(({ children, className, enableTransformer, fallbackClassName, language, style, theme }) => { const safeChildren = children ?? ""; const streaming = useStreamHighlight(safeChildren, { enableTransformer, language, streaming: true, theme }); const lines = streaming?.lines; const preStyle = streaming?.preStyle; const fadeRef = useRef(createTokenFadeStore()); const previousTextRef = useRef(""); if (!safeChildren.startsWith(previousTextRef.current)) fadeRef.current = createTokenFadeStore(); previousTextRef.current = safeChildren; const now = performance.now(); const lineStarts = lines ? markTokenBirths(fadeRef.current, lines, now) : []; if (!lines || lines.length === 0) return /* @__PURE__ */ jsx("div", { className: fallbackClassName, dir: "ltr", style, children: /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: safeChildren }) }) }); return /* @__PURE__ */ jsx("div", { className, dir: "ltr", style, children: /* @__PURE__ */ jsx("pre", { className: cx("shiki", theme), style: preStyle, tabIndex: 0, children: /* @__PURE__ */ jsx("code", { style: { display: "flex", flexDirection: "column", whiteSpace: "pre" }, children: lines.map((line, index) => /* @__PURE__ */ jsx(TokenLine, { fade: fadeRef.current, line, now, start: lineStarts[index] }, `line-${index}`)) }) }) }); }); StreamRenderer.displayName = "StreamRenderer"; //#endregion export { StreamRenderer as default }; //# sourceMappingURL=StreamRenderer.mjs.map