# 设计文档:GitHub 提示块的渲染方案 面向维护者。用户可见的行为见 [README.md](../README.md),开发与发版流程见 [CONTRIBUTING.md](../CONTRIBUTING.md)。 ## 1. 目标与约束 - 目标:DSH 回复里的 GitHub 提示块(`> [!NOTE]` 等五种)显示成 GitHub 的提示框,且随主题与界面语言变化。 - 约束一:**只改显示**。会话记录、事件流、导出的 Markdown 必须保持原文,因此不能在产生消息的环节改文本。 - 约束二:**不能覆盖宿主的对话渲染**。整节点覆盖会连带重写复制按钮、图片、流式渲染与折叠逻辑,等于把宿主行为复制一份。 - 约束三:插件不能因为一个提示块而弄坏界面。Client 插件入口失败会让整个 Web boot 失败(宿主的 `web boot: N entries did not activate`),所以 `apply` 必须不抛异常。 ## 2. 宿主现状(DSH 0.2.1-rc.5) - Markdown 渲染器是宿主内置的 mdast 渲染器:节点类型在固定的 `switch` 里渲染,`blockquote` → `
{children}`,`paragraph` → `
{children}
`。 - 没有可注册的块级渲染扩展点,也没有「助手正文」槽位。`conversation.chat.node` 的 `assistant-step` 是**整节点**渲染器槽位(`replaceRisk: shadows-shipped-ui`),替换它要自己重写助手消息行。 - Markdown 容器的子节点之间由渲染器插入 `\n` 文本节点(用于保持行内空白),因此「第一个元素子节点」是 `:first-of-type` 而不是 `:first-child`。 - `break` 节点渲染成 `"\n"``` | 依赖点 | 用法 | | --- | --- | | `blockquote` 的首个 `"[!NOTE]"
"\n"
"\n" "另外顺带修好了……"
` | 只处理这种形状;没有段落就不动 |
| 该段落 `firstChild` 是以 `[!TYPE]` 开头的文本节点 | 识别的唯一依据;大小写不敏感,允许前后空格与制表符 |
| 标记后紧跟 `
` | 该换行是空行,标记到 `data-dsh-gfm-alert-lead` 后由 CSS 隐藏 |
| 段落其余文本 | 原样保留,不再改写 |
任何一条不成立,插件就不动这个块——宁可退化成普通引用块,也不猜。
## 5. 装饰算法
1. 扫描:`apply` 时对 `document.body` 做一次全量扫描;此后由 `MutationObserver`(`childList`、`subtree`、`characterData`)增量处理新增子树与文本变化。
2. 识别:取 `blockquote` 首个 `
` 的 `firstChild`,用 `^[ \t]*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*` 匹配。
3. 写入:
- `data-dsh-gfm-alert="
` 且文本已空,给段落加 `data-dsh-gfm-alert-lead`。
4. 剥离:把标记从文本节点值里切掉,其余文本不动。
5. 幂等:第二次跑时匹配不到标记,只做标题同步(语言切换走 `locale/change` → 重新写 `label` 属性)。
6. 流式:宿主重新渲染可能把标记写回文本节点;`characterData` 记录会立刻再走一遍 3–4 步,不产生可见闪烁。
`MutationObserver` 也会收到插件自己剥离文本触发的回调,因此步骤 3–5 必须无副作用地重复执行。
## 6. 样式方案
- 提示框:容器规则给 `blockquote[data-dsh-gfm-alert]` 设圆角、内边距、底色与左侧 3px 色条。宿主对 `blockquote` 的默认规则是 `.