UIKit Markdown 渲染
状态:调研(Spike)+ chat-studio 侧缺口修复已落地(2026-09-22)。 成文背景:2026-09 技术扫描 TODO P1-5(原始信号:Stream Chat Android 6.44.0/7.11.0 在 Compose UI Kit 将消息文本按 Markdown 渲染;imcore 的 AI/流式回复天然 Markdown 密集)。
1. 各端现状盘点
| 端 | 现状 | 缺口 |
|---|---|---|
| chat-studio(web 演示端) | MarkdownText.tsx:react-markdown + remark-gfm(表格)+ remark-breaks;默认禁原始 HTML;默认 urlTransform 仅放行 http/https/mailto 等;链接强制新窗口 + noopener |
仅 senderID.startsWith('bot:') 走 Markdown——通用流消息(第三方 REST 生产者,如 agent:xxx)在打字机/最终态都按纯文本渲染。本轮已修复:streaming 本地标记 + envelope.stream 终态标记也触发 Markdown 渲染(含测试) |
| Flutter uikit | 纯 Text 渲染,无 Markdown 能力 |
bot/流式回复的代码块、列表、表格全部丢失排版——AI SKU 移动端观感的主要缺口 |
| web/imcore-sdk(TS SDK) | 无 UIKit 层渲染职责 | 不适用(SDK 层不渲染) |
2. 公共安全子集规范(各端实现必须一致)
允许:标题 #~######、加粗/斜体/删除线、行内代码、围栏代码块、有序/无序列表、
任务列表、引用、链接(http/https/mailto,新窗口 + noopener noreferrer)、
表格(GFM)、硬换行(GFM breaks)。
拒绝:原始 HTML / dangerouslySetInnerHTML 等价物、javascript:/data: URL、
脚本与内联事件、未知协议链接。
策略项(各端可配,默认值需对齐):
- 图片外链:当前 chat-studio 依赖 react-markdown 默认(允许 http(s) 图片)。跟踪像素
是真实风险(AI 回复场景图片罕见),建议默认禁用
<img>、以“关闭的开关”提供 (chat-studio 现行为暂保留,Flutter 实现时直接默认禁,后续统一); - 数学/LaTeX:暂不进子集(AI SKU 有需要再评估,避免引入解析面)。
半成品语法容错(流式过程中未闭合的代码围栏/表格):按 ChatGPT 惯例,未闭合部分 渲染为纯文本直至闭合,不报错不吞内容。chat-studio 由 react-markdown 全量重解析天然获得; Flutter 选型必须验证此行为。
3. Flutter 选型(2026-09 核实)
flutter_markdown(Google 一方)已于 2025 年停更(pub.dev 标注 discontinued),
不可选。可选:
| 候选 | 定位 | 评价 |
|---|---|---|
| flutter_markdown_plus(推荐) | flutter_markdown 的社区接续维护(Foresight Mobile),API 兼容,支持 basic/CommonMark/GFM/extended 四级 | 迁移成本最低、语法覆盖完整;流式半成品容错需 Spike 验证 |
| gpt_markdown | 面向 LLM 对话的 Markdown+LaTeX 渲染,含文本选择 | 对话场景契合,但 LaTeX 超出我们的安全子集,需关断 |
| flutter_markdown_stream / flutter_streaming_text_markdown | 流式专用:分 token 渐进渲染、无闪烁、未闭合语法优雅降级 | 与 uikit 自带的打字机排水功能重叠,需二选一避免双重动画 |
建议:以 flutter_markdown_plus 为基线做 Spike(渲染安全子集 + 半成品容错 +
长消息滚动性能),打字机动画继续用 uikit 自己的排水层(与 chat-studio 行为一致);
若 Spike 发现全量重解析在长消息(≥2k 字)下掉帧,再评估流式专用包。
来源:flutter_markdown 停更公告(pub.dev)、 flutter_markdown_plus 接续维护说明(Foresight Mobile)、 flutter_streaming_text_markdown(pub.dev)。
4. 落地路径(执行状态)
- 已完成:chat-studio 通用流消息 Markdown 渲染 + 3 条渲染测试;
- 已完成(2026-09-22 Spike 采信,直接落地):Flutter uikit 以
flutter_markdown_plus 1.0.12为基线实现ImcoreMarkdownText(安全子集:GFM 表格/任务列表/删除线;图片默认禁用;链接回调仅放行 http/https/mailto 且由宿主打开,不引入 url_launcher;内联 HTML 按纯 文本呈现)。接线语义与 chat-studio 一致:bot(bot:前缀)/流式气泡 (isStreamPending或envelope.stream)走 markdown,人类消息纯文本; 流式光标字符随排水追加、最终文本自然取代。6 条新测试,全套 268 绿, analyzer 零告警;golden 向量测试为纯逻辑比对,无像素基准影响。 - Spike 性能实测与本底结论:2,267 字符(40 段+代码块+表格)首次全量 构建+布局在 debug 模式约 310ms。典型 LLM 回复(<1k 字)无压力;超长 流式消息按 33ms 全量重解析会掉帧,现靠“按积压加速排水”(约 6-20 tick 收敛)缓解。后续项:若 AI SKU 常态产出 2k+ 字回复,再评估流式专用 渲染(flutter_markdown_stream 一类)或增量解析;
- 图片默认禁用已在 Flutter 端落地;chat-studio 端(react-markdown 默认 允许 http(s) 图)在两端统一策略时一并收紧。