前端AI 应用UI组件【免费下载链接】streamdownA drop-in replacement for react-markdown, designed for AI-powered streaming.项目地址https://gitcode.com/gh_mirrors/stre/streamdown点击查看免费下载streamdown/cjk是 Streamdown 生态中面向中文、日文、韩文CJK文本处理的官方插件解决 AI 流式渲染场景下两大痛点表意标点附近的强调标记粗体、斜体、删除线无法被 CommonMark/GFM 正确解析以及裸链接autolink吞掉尾随中文标点。本文以该插件的 CHANGELOG 演进为主线结合其源码实现与测试用例完整梳理从安装接入、功能原理到 v1.0.4 最新能力的实战细节读完即可在自己的流式聊天应用中原样落地。为什么流式渲染必须考虑 CJK 文本Streamdown 是一个面向 AI 流式输出的 react-markdown 替代方案见 主包 README而大语言模型在生成中文、日文、韩文内容时会自然地用**、*、~~包裹包含标点的短语。这带来两个由规范本身导致的渲染缺陷缺陷一强调标记与表意标点相邻时失效。CommonMark/GFM 规范存在一个已知限制上游 commonmark-spec issue #650当强调闭合标记紧挨句号、逗号、括号等表意标点时解析器可能不认为**构成强调。典型例子**この文は太字になりませんThis wont be bolded。**この文のせいでIt is due to this sentence。若不启用 CJK 处理这段文本会原样输出**用户看到的不是粗体而是字面星号。缺陷二autolink 吞掉尾随中文标点。GFM 会把https://example.com。整体识别为裸链接导致。被吸进 URL 并可能造成链接地址污染Check out https://example.com。这是一个链接。streamdown/cjk正是为这两个问题而生的专用插件官方文档位于 apps/website/content/docs/plugins/cjk.mdx在 Streamdown 的插件体系中与streamdown/code、streamdown/math、streamdown/mermaid并列。安装与快速接入npm install streamdown/cjkTailwind 扫描配置该插件与 Streamdown 主包一样依赖 Tailwind 检测其dist中的工具类需要把构建产物加入内容扫描范围。Tailwind v4—— 在globals.css或主 CSS 文件中添加source指令source ../node_modules/streamdown/cjk/dist/*.js;路径必须相对于你的 CSS 文件指向node_modules中的streamdown/cjk在 monoreponpm workspaces、Turbo、pnpm中依赖通常被提升到根node_modules需要相应调整../层级主包 README 的 Monorepo setup 一节有目录示意。Tailwind v3—— 在tailwind.config.js的content数组中加入module.exports { content: [ ./app/**/*.{js,ts,jsx,tsx,mdx}, ./node_modules/streamdown/cjk/dist/*.js, ], // ... rest of your config };monorepo 下改为../../node_modules/streamdown/cjk/dist/*.js。在 Streamdown 中使用最简单的方式是导入预配置好的cjk单例import { cjk } from streamdown/cjk; Streamdown plugins{{ cjk }} {markdown} /Streamdown需要独立配置时使用工厂函数createCjkPlugin()每次调用返回全新的插件实例测试createCjkPlugin一节验证了plugin1与plugin2互不共享remarkPluginsBefore/remarkPluginsAfter数组引用见 index.test.tsimport { Streamdown } from streamdown; import { createCjkPlugin } from streamdown/cjk; const cjk createCjkPlugin(); export default function Page() { return ( Streamdown plugins{{ cjk }} {markdown} /Streamdown ); }与 AI SDK 集成时官方示例把四个插件一并传入主包 README Usageimport { useChat } from ai-sdk/react; import { Streamdown } from streamdown; import { code } from streamdown/code; import { mermaid } from streamdown/mermaid; import { math } from streamdown/math; import { cjk } from streamdown/cjk; import katex/dist/katex.min.css; import streamdown/styles.css; export default function Chat() { const { messages, status } useChat(); return ( div {messages.map(message ( div key{message.id} {message.role user ? User: : AI: } {message.parts.map((part, index) part.type text ? ( Streamdown key{index} animated plugins{{ code, mermaid, math, cjk }} isAnimating{status streaming} {part.text} /Streamdown ) : null, )} /div ))} /div ); }核心能力一强调格式化粗体 / 斜体 / 删除线插件内部依赖两个上游 remark 插件声明于 package.json 的 dependenciesremark-cjk-friendly修正表意标点附近的强调解析处理粗体与斜体remark-cjk-friendly-gfm-strikethrough让 GFM 删除线在标点场景下也能正确闭合。三种强调格式在日、中、韩文本中均可正常工作官方文档的示例**日本語の文章括弧付き。**この文が後に続いても大丈夫です。 **中文文本带括号。**这句子继续也没问题。 **한국어 구문(괄호 포함)**을 강조. *これは斜体のテキストです括弧付き。*この文が後に続いても大丈夫です。 *这是斜体文字带括号。*这句子继续也没问题。 *이 텍스트(괄호 포함)*는 기울임꼴입니다. ~~削除されたテキスト括弧付き。~~この文は正しいです。 ~~删除的文字带括号。~~这个句子是正确的。 ~~이 텍스트(괄호 포함)~~를 삭제합니다.混合中英内容同样顺畅例如**重要提示Important Notice**请注意。会被正确渲染为粗体的“重要提示Important Notice”。支持的表意标点包括圆括号、方括号/引号类【】「」〈〉、句号。、逗号、、问号、感叹号、冒号。这一能力对 AI 输出的意义在端到端测试中得到了完整覆盖packages/streamdown/tests/cjk-friendly.test.tsx 中针对日文、中文、韩文分别验证了粗体、斜体、删除线、列表、表格、引用块以及“**ストリーミング”这类流式进行中的不完整强调依赖parseIncompleteMarkdown默认开启还有模拟 LLM 真实回答的“重要要点注意事项”结构。核心能力二Autolink 边界处理当 URL 后紧跟 CJK 标点时插件会把链接在标点处切开让标点以普通文本呈现链接落在干净的https://example.com上。支持的全部边界字符源码 index.ts 中CJK_AUTOLINK_BOUNDARY_CHARS集合测试逐一验证见 index.test.ts。、【】「」『』〈〉《》含全角句号、全角逗号、顿号、问号、感叹号、冒号、分号以及各类括号共 20 个字符。工作原理从 AST 角度拆解该能力由一个内置 remark 插件remarkCjkAutolinkBoundary实现处理流程如下确认是 autolink literalisAutolinkLiteral要求link节点恰好只有一个text子节点且其文本与node.url完全一致index.ts。因此自定义文本这类普通链接绝不会被误切。确认 URL 前缀AUTOLINK_PREFIX_PATTERN /^(https?:\/\/|mailto:|www\.)/i限定只处理 GFM 认可的自动链接协议index.tsftp://等保持原样。寻找边界findCjkBoundaryIndex按字符遍历 URL返回第一个 CJK 标点的偏移若标点位于 URL 起始位置或不存在则不处理。切分重建在边界处把原节点替换为“干净链接 尾随标点文本”。未开启该插件时例如仅使用remarkPlugins{[remarkGfm]}请访问 https://example.com。谢谢中链接文本会变成https://example.com。谢谢启用后链接文本与href均为https://example.com原文文本完整保留对照测试见 cjk-friendly.test.tsx。v1.0.4重新链接标点后的裸 URLCHANGELOG 中 1.0.4 的记录是本文重点——Re-linkify bare URLs that follow CJK punctuation when splitting GFM autolink literals (e.g. URLs separated by fullwidth semicolons)提交8cb3bc7。问题场景GFM 会把一长串用全角分号分隔的 URL 当成一个autolink。例如参考资料https://example.com/ahttps://example.com/bhttps://example.com/c旧版插件只在第一个处切开随后的裸 URL 会退化为纯文本。1.0.4 引入relinkifyTrailingText后三个 URL 全部重新成为独立链接保留为链接之间的独立文本对应回归测试见 index.test.ts注释明确指向 issue #623。实现要点复用 GFM 自身的校验逻辑relinkifyTrailingTextindex.ts没有重新解释 Markdown而是把尾随文本按 CJK 标点切分成若干片段每个片段放入独立的paragraph节点——独立段落可以防止 autolink 跨越 CJK 边界复用gfmAutolinkLiteralFromMarkdown().transforms来自mdast-util-gfm-autolink-literal即 GFM 官方的 URL 校验与 destination 规范化逻辑只把处理后段落的内联子节点插回原 AST标点与正文原样保留。两个值得注意的细节www.前缀片段保护若片段恰好等于www.等仅有前缀的文本会被AUTOLINK_PREFIX_PATTERN命中并跳过 GFM 的 text transform避免www.被无意义地改写成http://wwwdestination 规范化但 label 不变https://example.comwww.test.com处理后第二个链接的url规范化为http://www.test.com但显示文本仍是www.test.com见 index.test.ts。测试还覆盖了多种尾随片段组合index.test.ts尾随内容处理结果https://test.com/a).结束识别出https://test.com/aabchttps://test.com结束不产生链接前缀不合法www.invalid_结束不产生链接testexample.com结束识别为mailto:testexample.com**说明**https://test.com结束识别出https://test.com说明https://test.com结束识别出https://test.com表情符号、连续分号均安全插件 API 与执行顺序CjkPlugin的公开形状定义在 lib/plugin-types.ts与插件包内 index.ts 的导出一致interface CjkPlugin { // 必须在 remarkGfm 之前运行的插件如 remark-cjk-friendly修改 emphasis 处理 remarkPluginsBefore: Pluggable[]; // 必须在 remarkGfm 之后运行的插件如 autolink 边界切分、删除线增强 remarkPluginsAfter: Pluggable[]; // deprecated - 请改用 remarkPluginsBefore / remarkPluginsAfter remarkPlugins: Pluggable[]; }为什么强调顺序remark-cjk-friendly需要先于 GFM 运行以修正 emphasis 解析而remarkCjkAutolinkBoundary依赖 GFM 先生成 autolink 节点remark-cjk-friendly-gfm-strikethrough则增强 GFM 删除线——两者必须排在 GFM 之后。Streamdown 主包会自动编排这一顺序。mergedRemarkPlugins的组装逻辑在 packages/streamdown/index.tsxCJK before → 默认插件含 remarkGfm→ disableAutolinkProtocols → CJK after → math源码注释明确写着 Order: CJK before - default (remarkGfm) - CJK after - math。若脱离 Streamdown 手动集成必须遵守同一顺序remarkPluginsBefore先于remarkGfmremarkPluginsAfter后于remarkGfm。remarkPlugins仅是两者拼接的向后兼容字段新代码应使用拆分后的两个数组。从 CHANGELOG 看插件演进streamdown/cjk的版本历史很短但清晰每个补丁都是一次架构或能力升级版本变更影响1.0.1插件化commit0b80aed从 Streamdown 核心包中拆出为独立插件包1.0.2移除 CJS buildscommit6b42a85包改为纯 ESM与现代构建链对齐1.0.3上游依赖 v1.x → v2.xcommit6f1ea07v2.0.0 唯一破坏性变更是放弃 Node.js 16而 Streamdown 已要求 Node ≥18因此零影响1.0.4标点后裸 URL 重新链接commit8cb3bc7修复全角分号分隔 URL 退化为文本的问题版本 1.0.2 的“移除 CJS”在构建配置层面得到印证tsup.config.ts 中format: [esm]、platform: browser、external: [react, react-dom]且 package.json 的exports仅暴露import入口与.d.ts类型。1.0.3 的“Node.js ≥18”要求则来源于 changelog 对 v2.0.0 破坏性变更的说明。边界与安全不会误伤普通链接插件对非 autolink 场景保持克制这一点有大量测试兜底index.test.ts[链接](https://example.com。谢谢)普通链接保留完整 URL不会被切分[Visit **here**](https://example.com。test)含多个子节点的链接不视为 autolink literalftp://等非 GFM 协议不参与处理标点在 URL 开头如。https://example.com时跳过防御性守卫visit回调对无父节点parent为undefined的根级link节点直接返回避免异常。流式场景下的文本完整性同样有保证无论怎么切分getTexts(tree).join()都与原始 Markdown 输入完全一致也就是说插件只改变 AST 结构绝不增删用户文本。总结streamdown/cjk以两个上游 remark 插件加一个自研 autolink 边界插件补齐了 CJK 文本在流式 Markdown 渲染中的两块短板。理解其remarkPluginsBefore/remarkPluginsAfter的相位设计、v1.0.4 的重新链接策略以及“只改 AST 不改文本”的约束你就能在 AI 聊天应用中放心地为中文、日文、韩文输出开启它并与代码高亮、数学公式、Mermaid 等插件无缝组合。赞分享前端AI 应用UI组件【免费下载链接】streamdownA drop-in replacement for react-markdown, designed for AI-powered streaming.项目地址https://gitcode.com/gh_mirrors/stre/streamdown点击查看免费下载相关推荐Prettier 中日韩CJKMarkdown 文本格式化splitCjkText 测试集与空白处理机制全解析Prettier 中日韩CJKMarkdown 文本格式化splitCjkText 测试集与空白处理机制全解析 本篇指南以 Prettier 仓库中的 M开发工具格式化CLIPDFminer.six CJK语言支持终极指南中、日、韩PDF文本处理全解析PDFminer.six CJK语言支持终极指南中、日、韩PDF文本处理全解析 PDFminer.six是一款强大的PDF文本提取工具特别针对中、日、韩C后端Streamdown国际化支持多语言与CJK字符渲染最佳实践Streamdown国际化支持多语言与CJK字符渲染最佳实践 Streamdown作为react markdown的替代品专为AI驱动的流式内容设计提供了前端AI 应用UI组件上一篇KServe KnativeStatus 模型深度解析从 Knative 条件传播到 Python SDK 状态读取下一篇Apache Storm Trident Spout 完全指南批次源、事务语义与分区读取实现创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?