关于 pr-review-canvas
用于生成 GitHub pull request 交互式 HTML 评审页面的技能。它通过摘要、文件注释、差异展示和可选图示,帮助评审者理解变更。
主要功能
- 01获取 PR 元数据与文件
- 02汇总变更内容
- 03展示带注释的文件区块
- 04渲染并分类差异
- 05支持图示与检查清单
适用场景
- 01准备引导式 PR 评审
- 02解释复杂代码改动
- 03标示移动代码或样板代码
- 04分享本地交互式评审页
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cursor/ plugins --skill pr-review-canvas安装后在 Claude Code 中输入/pr-review-canvas确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
renderer.jsstyles.csstemplate.html详细介绍
PR 评审画布
生成一份交互式 HTML,评审 GitHub PR,仿佛有位同事正带你了解其中的重点。
工作流
1. 获取 PR 数据
并行运行以下 gh api 调用:
gh api repos/{owner}/{repo}/pulls/{number} --jq '{title, body, user: .user.login, state, additions, deletions, changed_files, base: .base.ref, head: .head.ref}'
gh api repos/{owner}/{repo}/pulls/{number}/files --paginate --jq '.[] | {filename, status, additions, deletions, patch}'
gh api repos/{owner}/{repo}/pulls/{number}/comments --jq '.[] | {user: .user.login, body, path, line}'
2. 分析 PR 并编写正文 HTML
阅读差异,理解 PR,并直接以 HTML 编写 <body> 内容。你可以充分发挥创意——目标是向评审者清楚地说明这个 PR。选择最适合该 PR 的结构。
常见结构(按需调整):
- 包含标题、PR 编号、作者和统计数据的页眉
- 用通俗语言说明 PR 内容的摘要框
- 带注释和差异内容的核心文件分区
- 默认折叠的机械性或样板文件
- 底部的评审清单
你也可以添加:
- 伪代码摘要,用于冗长代码——用通俗语言或简短伪代码展示算法,并在下方折叠显示实际差异(使用标注为“显示完整实现”的
.bp-section卡片)。如果 150 行重试、退避和错误处理代码实际上只是“使用指数退避和熔断器进行请求”,这种方式就很有帮助。 - 图表(内联 SVG、通过 CDN 引入的 mermaid、
<pre>中的 ASCII 图) - 展示前后控制流的流程图
- 对比新旧行为的表格
- 用于提示警告、问题或注意事项的提示框
- 有助于理解的交互式小部件
- 任何其他能让评审更清晰的内容
伪代码模式示例:
<div class="file-card">
<div class="file-hdr" onclick="toggle(this)">
<span class="fname">retryClient.ts</span>
<div class="fstats"><span class="pill add">+173</span><span class="pill del">−11</span><span class="chev open">▶</span></div>
</div>
<div class="file-body open">
<div class="file-note">
<strong>What this does in plain English:</strong>
<pre style="margin-top:8px;color:var(--text);font-size:12px;line-height:1.6;">
fetch(url):
if circuit breaker is open → fail fast
retry up to N times:
try fetch with timeout
on success → close circuit breaker, return
on retryable error → wait (exponential backoff + jitter)
on non-retryable error → throw
circuit breaker records failure</pre>
</div>
<div class="bp-section" style="margin:0;border:0;border-radius:0;">
<div class="bp-hdr" onclick="toggleBP(this)">
<span>Show full implementation (+173 lines)</span><span class="chev">▶</span>
</div>
<div class="bp-body"><div data-diff="retryClient"></div></div>
</div>
</div>
</div>
3. 可用的 CSS 类和 JS 实用工具
阅读此技能目录中的 styles.css 和 renderer.js。它们提供了一套预先构建的暗色主题工具。将它们原样注入 template.html。
可使用的 CSS 类:
| 类 | 用途 |
|---|---|
.header、.header h1、.header-meta | 页面页眉 |
.pill.add、.pill.del、.pill.files | 统计徽标(+N、-N、N 个文件) |
.content | 居中内容容器(最大宽度 900px) |
.summary | 摘要/TL;DR 框 |
.section-title | 带底部边框的章节标题 |
.ic | 行内代码引用(等宽字体、蓝色、深色背景) |
.file-card、.file-hdr、.file-body | 可折叠文件卡片(在 .file-hdr 上使用 onclick="toggle(this)") |
.file-note | 文件卡片内的固定审阅注释 |
.bp-section、.bp-hdr、.bp-body | 折叠式样板代码卡片(使用 onclick="toggleBP(this)") |
.bp-note | 样板代码卡片内的注释 |
.verdict | 审阅检查清单框 |
可用的 JS 函数:
| 函数 | 用法 |
|---|---|
toggle(hdrElement) | 展开/折叠 .file-body |
toggleBP(hdrElement) | 展开/折叠 .bp-body |
renderDiff(target, diffInput) | 渲染统一差异。target 可以是 DOM 元素、字符串 ID 或 CSS 选择器。diffInput 可以是原始补丁字符串或行数组,两者均可。会自动过滤导入语句、折叠纯空白变更,并检测移动的代码(蓝色/紫色着色)。 |
esc(string) | 对字符串进行 HTML 转义 |
渲染差异 — 使用 data-diff 属性自动发现。
在正文 HTML 中需要渲染差异的位置放置 <div data-diff="KEY"></div> 占位元素。页面加载后,渲染器会自动查找这些元素,并从 template.html 中的 <script id="pr-diffs-json" type="application/json"> 元素填充内容。
重要:补丁字符串除了包含换行符、反斜杠和引号外,还可能包含 </script>。 即使使用 json.dumps(...),如果将原始输出粘贴到可执行的 <script> 中也不够安全,因为 HTML 解析可能会提前结束标签。切勿手动将补丁字符串嵌入 JS/JSON。请改用以下安全方法:
- 在获取步骤中,使用
jq将补丁保存到 JSON 文件(它能正确处理转义):
gh api repos/{owner}/{repo}/pulls/{number}/files --paginate \
--jq '[.[] | {key: (.filename | gsub("[^a-zA-Z0-9]"; "_")), value: (.patch // "")}] | from_entries' \
> /tmp/pr-patches-{number}.json
- 在组装时,使用 Python 将 JSON 安全地注入
template.html:
python3 <<'PY'
import json
from pathlib import Path
patches = json.loads(Path('/tmp/pr-patches-{number}.json').read_text())
html = Path('/tmp/pr-review-{number}-body.html').read_text()
css = Path('styles.css').read_text()
js = Path('renderer.js').read_text()
tmpl = Path('template.html').read_text()
# Prevent literal </script> from terminating HTML script tags early.
safe_json = json.dumps(patches).replace('<', '\\u003c').replace('>', '\\u003e').replace('&', '\\u0026')
out = (
tmpl.replace('/* INJECT_CSS */', css)
.replace('/* INJECT_JS */', js)
.replace('', html)
.replace('{"__PR_DIFFS_PLACEHOLDER__":true}', safe_json)
)
Path('/tmp/pr-review-{number}.html').write_text(out)
PY
这样可以确保 JSON 有效,并安全地将内容嵌入 HTML 脚本中。Agent 会将正文 HTML 写入临时文件,然后由 Python 安全地组装所有内容。
差异数据的键应与 HTML 中 data-diff 属性的值匹配:
<div data-diff="path_to_file_ts"></div>
由于 renderer.js 在 <head> 中加载,如果需要用于自定义场景,也可以直接从行内 <script> 标签调用 renderDiff(target, lines)。该函数接受 DOM 元素、ID 字符串或 CSS 选择器作为 target,并接受字符串或数组作为 lines。
你不必局限于这些内容。 可以添加自己的行内 <style> 块、<script> 块、SVG、图表或其他任何内容。预先构建的组件能节省时间,但不会限制你。
4. 组装并提供服务
-
将正文 HTML(即所有放在
<body>内的内容)写入/tmp/pr-review-{number}-body.html -
使用上面第 3 步中的
jq命令,将补丁保存到/tmp/pr-patches-{number}.json -
运行上面第 3 步中的 Python 组装脚本(从此技能目录读取 styles.css、renderer.js、template.html,安全注入正文和补丁,并写入最终 HTML)
-
在固定端口上启动本地服务:
cd /tmp && python3 -m http.server 8432 --bind 127.0.0.1在后台运行此命令,然后在应用内浏览器中打开
http://127.0.0.1:8432/pr-review-{number}.html。为何使用固定端口并执行
cd /tmp: 后台 Shell 没有 TTY,因此 Python 会无限期缓冲其启动消息(“Serving HTTP on...”);使用端口 0 就永远无法得知分配了哪个端口。并且,虽然--directory /tmp可用,但cd /tmp在不同 Python 版本中更可靠。如果端口 8432 已被占用,请尝试 8433、8434 等。
Diff 功能(由 renderer.js 自动处理)
- 过滤仅包含导入语句的行
- 将仅有空白字符的更改折叠为上下文行
- 检测移动的代码块(在一处连续删除 3 行或更多,并在其他位置原样添加)——以蓝色/紫色而非红色/绿色呈现
- 近似匹配项(移动后有少量编辑)会使用不同的紫色色调
样式说明
- 深色主题:背景为
#1a1a1a,正文使用 Inter 字体,代码使用 IBM Plex Mono 字体 - 橙色使用
var(--warning),绿色使用var(--success),红色使用var(--danger),蓝色使用var(--accent) - 文件标题栏(
position: sticky; top: 0)和注释(top: 35px)会在滚动时固定 - 核心文件默认展开(
.file-body.open),机械生成的文件默认折叠