用 Streamdown 优雅地打字输出 AI 流式返回的文本

如果眼睛足够敏锐,你会发现有些 AI tool 通过 SSE 返回流式文本时,会先打印出 * ** - 或其它 Markdown 格式化字符,之后又迅速消失。用户阅读过程中,格式化的不同导致不同程度的 layout shift,文本不停换位置或忽大忽小,造成视觉疲劳。

问题

在网页中,我们通常用 react-markdown 来解析 Markdown 代码,一次性整体解析是没问题的,但 SSE 零零散散地把字串返到页面 Markdown 未成功解析时,用户就会看到奇奇怪怪的字符先。

解决办法

  • 写正则,修正各种 Markdown 语法不正确的场景;
  • 直接用 package Streamdown

代码

js 复制代码
// Nextjs

'use client';

import { useChat } from '@ai-sdk/react';
import { useState } from 'react';
import { Streamdown } from 'streamdown'; // 引入 streamdown

export default function Page() {
  const { messages, sendMessage, status } = useChat();
  const [input, setInput] = useState('');

  return (
    <>
      {messages.map(message => (
        <div key={message.id}>
          {message.parts.filter(part => part.type === 'text').map((part, index) => (
          
            { /* 用 Streamdown 包住 Markdown 文本 */ }
            <Streamdown key={index}>{part.text}</Streamdown>
          ))}
        </div>
      ))}

      <form
        onSubmit={e => {
          e.preventDefault();
          if (input.trim()) {
            sendMessage({ text: input });
            setInput('');
          }
        }}
      >
        <input
          value={input}
          onChange={e => setInput(e.target.value)}
          disabled={status !== 'ready'}
          placeholder="说点什么..."
        />
        <button type="submit" disabled={status !== 'ready'}>
          提交
        </button>
      </form>
    </>
  );
}

解决的痛点

内置的排版样式

Streamdown 为常见的 Markdown 提供了内置的 Tailwind 类,自动格式化标题、列表、代码块等。

GitHub 风格的 Markdown

Streamdown 默认支持 GitHub 风格的 Markdown (GFM),可以正常解析任务列表、表格等。

美观、交互式的代码区块

Streamdown 使用 Shiki 来高亮显示代码块,自带复制按钮,悬停显示复制按钮!

数学表达式

Streamdown 通过 remark-math 和 KaTeX 支持 LaTeX 数学表达式,在 Markdown 中完美展现各种数学符号。韦神满意地笑了!

提前解析未闭合的 Markdown 标签

Streamdown 提前解析未闭合的 Markdown 标签(# 标题、内联代码**粗体**_斜体_[链接]()等),极大提升了用户体验,大大减少了文本来回抖动的现象。

内置安全机制

Streamdown 检测 Markdown 中不安全的元素,自动屏蔽可能已 被 prompt 注入攻击 的图像和链接。

演示仓库

以上都为静态图片,未能动态演示出打字过程中的变化,checkout Demo of Streamdown 到本地试下。Have fun :)

相关推荐
光影341516 小时前
专利撰写与申请核心要点简报
前端·数据库·php
ze_juejin16 小时前
Angular 中设置宿主元素(Host)样式的方法
前端
用户904438163246016 小时前
《零代码基础也能 AI 创作?GPT+DALL・E 实战教程,10 分钟上手》
前端·github
WillaWang16 小时前
aria | "Accessible Rich Internet Applications"
前端
reoreo16 小时前
如何使用 i18next 实现多种语言的国际化(从新建 vite ts 项目开始)
前端·javascript
云动雨颤16 小时前
Typecho 博客统计脚本怎么装?同步 / 异步 + Head/Body 选择指南
前端·html
用户40993225021216 小时前
快速入门Vue3的v-指令:数据和DOM的“翻译官”到底有多少本事?
前端·ai编程·trae
Asort16 小时前
JavaScript设计模式(二十三)——访问者模式:优雅地扩展对象结构
前端·javascript·设计模式
星辰h16 小时前
基于JWT的RESTful登录系统实现
前端·spring boot·后端·mysql·restful·jwt
要加油哦~16 小时前
前端笔试题 | 整理&总结 ing | 跨域 + fetch + credentials(携带cookie)
前端