@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
82 lines (80 loc) • 2.47 kB
JavaScript
import { visit } from "../../node_modules/unist-util-visit/lib/index.mjs";
//#region src/Markdown/plugins/rehypeStreamAnimated.ts
const BLOCK_TAGS = new Set([
"p",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"li"
]);
const SKIP_TAGS = new Set([
"pre",
"code",
"table",
"svg"
]);
function hasClass(node, cls) {
const cn = node.properties?.className;
if (Array.isArray(cn)) return cn.some((c) => String(c).includes(cls));
if (typeof cn === "string") return cn.includes(cls);
return false;
}
const rehypeStreamAnimated = (options = {}) => {
const { charDelay = 20, fadeDuration = 150, baseCharCount = 0, revealed = false, timelineElapsedMs } = options;
const hasTimeline = typeof timelineElapsedMs === "number" && Number.isFinite(timelineElapsedMs);
return (tree) => {
let globalCharIndex = 0;
const shouldSkip = (node) => {
return SKIP_TAGS.has(node.tagName) || hasClass(node, "katex");
};
const wrapText = (node) => {
const newChildren = [];
for (const child of node.children) if (child.type === "text") for (const char of child.value) {
const relativeIndex = globalCharIndex - baseCharCount;
let className = "stream-char";
let delay;
if (revealed) className = "stream-char stream-char-revealed";
else if (hasTimeline) {
const progress = timelineElapsedMs - globalCharIndex * charDelay;
if (progress >= fadeDuration) className = "stream-char stream-char-revealed";
else delay = -progress;
} else if (relativeIndex >= 0) delay = relativeIndex * charDelay;
else {
const elapsed = -relativeIndex * charDelay;
if (elapsed >= fadeDuration) className = "stream-char stream-char-revealed";
else delay = -elapsed;
}
const properties = { className };
if (delay !== void 0 && delay !== 0) properties.style = `animation-delay:${delay}ms`;
newChildren.push({
children: [{
type: "text",
value: char
}],
properties,
tagName: "span",
type: "element"
});
globalCharIndex++;
}
else if (child.type === "element") {
if (!shouldSkip(child)) wrapText(child);
newChildren.push(child);
} else newChildren.push(child);
node.children = newChildren;
};
visit(tree, "element", ((node) => {
if (shouldSkip(node)) return "skip";
if (BLOCK_TAGS.has(node.tagName)) {
wrapText(node);
return "skip";
}
}));
};
};
//#endregion
export { rehypeStreamAnimated };
//# sourceMappingURL=rehypeStreamAnimated.mjs.map