docs/06-decisions/ADR-0003-markdown-相对路径图片处理边界.md
已采纳
2026-07-04
GSY 用 gsy_markdown_widget.dart 渲染 issue / PR / comment / review body 里的 markdown。这个 widget 有一个 baseUrl 参数,理论上用于把 body 里的相对路径图片(例如 )拼成绝对 URL 再交给 Image.network。
现状(在此 ADR 之前):
https://raw.githubusercontent.com/USER/REPO/BRANCH/,行为正确baseUrl 全部硬编码为 ""一次 review 中提出"baseUrl:"" 是理论洞,未来相对路径图片会走 Image.file 抛 FileSystemException"。作者据此引入 helper getGithubWebRepoBaseUrl,把三个 widget 的构造函数加 repoUserName? / repoName? 参数,一路从 IssueDetailPage 和 repository_detail_issue_list_page.dart 串下去,拼出 https://github.com/USER/REPO/。
独立 reviewer 复审时顺着 _processMarkdownImages → kDefaultImageBuilder 走了一遍真实数据流:
baseUrl:"" 场景下,相对路径最终会命中 kDefaultImageBuilder 里的 if (uri.scheme.isEmpty) return const SizedBox();,不会走到 Image.file。行为是"静默显示为空白",不是"抛异常崩溃"。https://github.com/USER/REPO/foo.png 这种 URL,GitHub Web 不会返回 302 到 CDN,也不会返回图片内容。它会返回一个 HTML 页面,Image.network 拿到 HTML 会解析失败白屏。这比 baseUrl:"" 走 SizedBox 视觉上更糟。raw.githubusercontent.com/USER/REPO/BRANCH/foo.png 才是符合 GitHub CDN 语义的 base,但 issue / PR body 的上下文里没有 branch 信息(不像 README 有 default_branch),无法可靠拼出。硬编码 main / master 都会对相当一部分老仓库或非默认分支的相对引用错位。user-attachments/... 或直接绝对 URL,所以真实数据里几乎不存在需要 baseUrl 参与解析的相对路径。引入 helper 反而把一个"不会崩、几乎不触发"的路径改成一个"命中就白屏"的路径,是用错的方案修一个不存在的问题。回滚。
从现在开始约束 markdown 相对路径图片的处理边界:
issue / PR / comment / review body 场景,GSYMarkdownWidget.baseUrl 一律传 ""。
SizedBox 静默失败,不会崩溃。baseUrl:"" 是故意为之的行为,不是待修复的 TODO。README / 源码文件预览场景,继续用 getRawBaseUrl。它拼出的 raw.githubusercontent.com/USER/REPO/BRANCH/ 是 GitHub CDN 真正支持的路径。这一场景本来就有 branch 信息,helper 契约完整。
不引入 "GitHub Web 仓库根 URL" 类型的 helper(例如 github.com/USER/REPO/)。这种 URL 不是 CDN 入口,Image.network 无法从它加载图片。
若未来出现真实用户反馈"某评论里的图片显示不出来",先抓具体的 body 内容做 fixture、写单测、再做修复。不要基于"理论洞"引入防御代码。
好处:
baseUrl:"" 就想串参数"这类基于经验主义的错误修复循环代价:
)会显示为空白。用户看不到内容也不知道原因。若这种反馈真的出现,按第 4 条走 fixture-first 流程。getFooBaseUrl 这种名字很容易让人以为"齐全时就能用"。实际契约取决于下游消费方(这里是 Image.network)而不是 helper 自己。命名必须能反映"这个 URL 真的能被目标消费方消费"。baseUrl:"" 的实质语义不是 TODO:它是"放弃解析相对路径 → 下游走 SizedBox",这是一条有意的行为路径,不是 bug。改动前必须先读 _processMarkdownImages 和 kDefaultImageBuilder 全数据流。