Swift Markdown Kit · DOCS
点击交互
点击交互
读者点链接、点图片、点复制、点重试时会发生什么,以及怎么把其中任意一项接管 过来。
划线规则
SDK 只有一条判断标准:这个动作是否离开渲染区域?
| 交互 | 谁来做 | 能改吗 |
|---|---|---|
| 复制(代码块、消息) | SDK,始终 | 行为不能改,是否显示可以。 |
| 推理折叠 | SDK,始终 | 文案和默认状态可以改。 |
| 任务勾选 | SDK,始终 | —— |
| 链接 | 默认 SDK | 可以,改一行。 |
| 图片 | 默认 SDK | 可以,改一行。 |
| 重试、编辑 | 始终是你 | SDK 无法重新发起你的请求。 |
| 自定义语法点击 | 始终是你 | —— |
复制和折叠不需要 App 提供任何信息,把它们交给你只是徒增工作量。而打开链接或 图片会离开渲染区域,那里你的 App 有理由拥有最终决定权。
链接与图片
什么都不做,两者就能用:链接在应用内 SFSafariViewController 打开,图片在
全屏预览器打开,支持双指缩放、双击放大、下拉关闭。
想接管其中一项:
var options = MarkdownChatRenderOptions()
options.interaction.images = .handledByHost // 链接仍由 SDK 处理
MarkdownChatRenderView(messages: messages, options: options) { event in
guard case .action(let event) = event,
case .imageTapped(let url) = event.action, let url else { return }
presentMyOwnViewer(url)
}
每一项各有三种策略:
| 策略 | 行为 | 你收到什么 |
|---|---|---|
.automatic(默认) | SDK 在应用内打开。 | didOpenLink / didPresentImage —— 通知。 |
.handledByHost | 什么都不做。 | linkTapped / imageTapped —— 不处理就没有反应。 |
.disabled | 什么都不做,且元素不再显示为可点击。 | 什么都不发。 |
options.interaction = .manual 可以一次把两项都设成 .handledByHost。
通知不是否决
两类事件被刻意区分开:
- 请求 ——
linkTapped、imageTapped、extensionTapped。SDK 什么都没做, 你不处理就没有任何反应。 - 通知 ——
didOpenLink、didPresentImage、codeCopied、reasoningToggled。SDK 已经做完了,你再处理一次就会打开两次。
没有任何回调可以取消一个自动行为。想控制它,就改策略 —— 策略就是为此存在的。
链接安全
渲染的内容是模型输出,而模型是可以被诱导吐出 javascript:、file://、或者
指向你自己 App 的深链的。因此,不在白名单内的 scheme
既不会被打开,也不会交给你:
options.interaction.allowedLinkSchemes // 默认 ["http", "https", "mailto"]
options.interaction.allowedLinkSchemes.insert("myapp")
其余一律以 linkBlocked(url:scheme:) 上报,方便你记录。这条规则在所有策略下
都生效,包括 .handledByHost —— 接管了链接处理,也不构成把 javascript:
递给你的理由。
两个渲染器还会拒绝任何可能替换掉渲染页面的导航。链接点击在页面内就被取消并走 bridge,所以真正抵达导航层的跳转,按定义就是异常。
复制
复制从不外包:SDK 写入剪贴板,然后告诉你结果。可配置的是这个入口是否存在 —— 有些 App 出于数据防泄漏要求必须能移除它:
options.interaction.allowsCodeCopy = false // 代码块不显示复制按钮
options.messageActions.assistantActions = [] // 消息下方不显示工具条
MarkdownChatRenderView(messages: messages) { event in
guard case .action(let event) = event else { return }
if case .codeCopied = event.action { showToast("已复制") }
if case .codeCopyFailed(_, _, let error) = event.action { showToast(error.message) }
}
重试与编辑
SDK 无法重新发起请求,也无法写进不属于它的输入框,所以这两个按钮只有你列出时
才会出现 —— 而列出它们,就是你承诺会处理 messageAction。见
流式与会话。
自定义语法
点击已注册的行内扩展 —— @某人、[[wikilink]] —— 会以
extensionTapped(name:action:value:) 上报,始终由你来路由。见
主题与语法。