AI 消息“复制纯文本”:复制时剥掉 Markdown 标记

举报
yd_296967770 发表于 2026/11/13 08:52:55 2026/11/13
【摘要】 AI 消息点“复制”粘出来的全是 Markdown 标记,用户要的是能直接贴进微信的干净文字。这篇是待做方案:不用正则剥标记,而是复用 md.js 已解析好的 blocks 结构逐块取文本,链接只留文字、代码原样保留;并讨论了覆盖现有“复制”还是另加入口的选择。

页面上的“复制”按钮今天点下去,粘贴出来的东西带着一身 Markdown 标记:**加粗**、# 标题、反引号、链接的 [文字](地址),全都原样跟过来了。用户想要的其实很简单:复制一段能直接贴进微信、备忘录、文档里的干净文字。这篇写的是待做方案,不是已上线功能的复盘,先把这句话说在前面——v3 里这个功能还没做。

现状:复制的是原文,不是看到的文字

先看现在页面上的真实状态。AI 消息下面的“复制”走的是 copyMessage:

copyMessage(e) {
  const idx = Number(e.currentTarget.dataset.index);
  const m = this.data.messages[idx];
  if (!m || !m.text || m.typing) return;
  wx.setClipboardData({ data: m.text });
}

它复制的是 m.text,也就是服务端返回的 Markdown 原文。页面上渲染出来是漂亮的加粗标题和列表,剪贴板里却是另一副样子:

## 今日安排
- **重点**:先做 `checkAlive` 的续接
- 参考 [成长中心](https://developer.huaweicloud.com/grow)

贴到微信里,对方看到的就是这堆符号。更别扭的是,这个复制还没有任何反馈——同样是复制,代码块的 copyCode 和链接的 openLink 复制完都会弹一句“代码已复制”“链接已复制”,copyMessage 复制完什么都不说,用户点完只能自己去别处粘贴验证。

顺带说一句,页面上另外两个复制不是这个问题:copyPrompt 复制的是提示词卡片的正文,copyCode 复制的是整段代码——代码本来就该原样复制,标记就是内容。需要剥标记的,只有 AI 消息正文这一种。

第一难:别用正则去剥 Markdown

最直觉的做法是写几行正则,把 **、#、反引号替换掉。但 Markdown 的标记不是平铺的符号,它是嵌套结构,正则一遇到下面这几种情况就翻车:

  • 嵌套格式:**加粗里带 代码**,先剥哪个都会留一半;
  • 链接:[文字](地址),想要的是“文字”,正则容易把地址也留下来,或者把括号吃错;
  • 代码围栏:围栏里的 ** 不是标记,是代码本身,剥了就改了代码;
  • 表格:| a | b | 剥掉竖线之后,两列会粘成一串,分不清谁是谁。

这些坑,md.js 在解析的时候其实已经一个个处理过了:围栏信息串宽松匹配、U+2028/2029 当换行、转义占位符、解析循环兜底前进。它输出的是一棵现成的结构——parseMarkdown 返回 blocks[],块分标题、段落、代码、表格、列表,块里再分 parseInlines 的行内片段。剥标记最稳的做法不是再写一套正则,而是沿着这棵已经解析好的结构,把每一段的文字取出来。

方案:顺着 blocks 走一遍,只取文字

思路是给 md.js 再加一个导出函数,比如 stripMarkdown(text),内部先调已有的 parseMarkdown,再按块类型取文本:

  • 标题、段落:把行内片段的文字拼起来,标记自然就没了;
  • 代码块:原样保留代码内容——代码里的符号不是标记,不能动;
  • 列表:每项取文字,项之间换行,序号或圆点可以保留一个简单的 - ;
  • 表格:每行各单元格的文字用空格或制表符隔开,行与行换行,竖线不要了;
  • 链接:只取链接文字,地址丢掉;图片:取 alt 文字,地址丢掉;
  • 行内代码:取反引号里的原文,不加反引号。

这样剥出来的文本,和页面上看到的文字是一致的,因为它们来自同一棵解析树。解析器以后修了什么边界情况,剥标记跟着受益,不用两头维护。

调用点只需要改一行:copyMessage 里把 m.text 换成剥完的结果:

wx.setClipboardData({ data: stripMarkdown(m.text) });

再顺手补上 copyCode 同款的成功提示,复制完弹一句“已复制”,用户就不用再跑到别处验证。失败和生成中的守卫不用动:m.typing 时本来就不给复制,失败消息没有正文可复制。

一个要先定下来的选择:覆盖“复制”,还是另加一个入口

这里有个产品选择,得先定下来再动手:

  • 方案 A:把现在的“复制”直接改成复制纯文本。入口不增加,用户不用学新东西;代价是想复制 Markdown 原文的人(比如要贴回编辑器继续改)没路了。
  • 方案 B:保留“复制”(原文),旁边再加一个“复制纯文本”。两条路都有,代价是消息下面的操作区多一个按钮,本来就已经有复制和重试了,再挤会显得乱。

我的倾向是 A。理由有两个:第一,大多数用户点“复制”的时候,想要的就是贴出去能直接读的文字,原文标记对他们是噪音;第二,代码块已经有单独的“复制代码”,真需要原文的人,大段内容基本都在代码块里,损失很小。如果以后真有人要 Markdown 原文,再加长按菜单也不迟。

落地清单:一个函数、一个调用点

  • md.js 新增 stripMarkdown:复用 parseMarkdown 的 blocks 结构,按块类型取文本,链接只取文字、代码原样保留;
  • copyMessage 改传剥完的文本,补成功 Toast;
  • 兜底:剥完为空(比如消息只剩一张图)时回退到原文,别让用户复制到一个空剪贴板;
  • 自测三组:嵌套格式加链接的段落、带代码围栏的回答、带表格的回答,粘贴到微信和备忘录里看一眼是不是干净文本。

满打满算就是一个函数加一个调用点,难的不是代码量,是想清楚“剥标记”这件事该由谁来做——答案是已经把 Markdown 吃透了的解析器,而不是新写的一堆正则。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。