关于 pierre-guard
pierre-guard 是用于检查 Plannotator 与 @pierre/diffs 集成的技能,面向维护差异审阅界面的开发者。它聚焦于依赖 Shadow DOM 内部选择器、CSS 变量及网格布局的兼容性检查。
主要功能
- 01核对组件属性与类型
- 02检查 Shadow DOM 选择器
- 03验证 CSS 变量及主题
- 04检查分栏网格布局假设
- 05按清单测试构建与运行
适用场景
- 01升级 @pierre/diffs 前检查 API 变化
- 02审阅差异查看器相关修改
- 03验证分栏和统一视图
- 04排查样式或布局兼容问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ backnotprop/ plannotator --skill pierre-guard安装后在 Claude Code 中输入/pierre-guard确认已加载。
详细介绍
Pierre 集成保护
Plannotator 的代码审查 UI 封装了 @pierre/diffs——这是一个使用 Shadow DOM 的开源差异渲染器。集成集中在单个文件中,但依赖一些未记录的内部实现(Shadow DOM 选择器、CSS 变量名称、网格布局假设)。此技能有助于验证更改不会破坏这些约定。
事实来源
- 上游仓库:https://github.com/pierrecomputer/pierre/tree/main/packages/diffs
- 本地类型:
node_modules/@pierre/diffs/dist/(.d.ts文件) - 集成点:
packages/review-editor/components/DiffViewer.tsx - 当前版本:查看
packages/review-editor/package.json中锁定的版本
务必根据上游仓库或本地 .d.ts 文件进行核实,不要依赖对 API 结构的记忆。
导入内容
import { FileDiff } from '@pierre/diffs/react';
import { getSingularPatch, processFile } from '@pierre/diffs';
这就是全部三个导入。DiffViewer.tsx 是唯一使用 Pierre 的文件。
需要保护的 API 表面
1. 组件属性(FileDiff)
从 node_modules/@pierre/diffs/dist/react/index.d.ts 或上游源码中读取当前属性类型。我们使用的属性如下:
| 属性 | 类型 | 说明 |
|---|---|---|
fileDiff | FileDiffMetadata | 来自 getSingularPatch() 或 processFile() |
options | FileDiffOptions<T> | 参见下方选项表 |
lineAnnotations | DiffLineAnnotation<T>[] | { side, lineNumber, metadata } |
selectedLines | SelectedLineRange | null | { start, end, side } |
renderAnnotation | (ann) => ReactNode | 自定义行内注释渲染器 |
renderHoverUtility | (getHoveredLine) => ReactNode | 悬停时显示的 + 按钮(上游已弃用——留意其移除) |
2. 选项对象
| 选项 | 传入的值 | 风险 |
|---|---|---|
themeType | 'dark' | 'light' | 低——标准枚举 |
unsafeCSS | CSS 字符串 | 高——会定位内部选择器 |
diffStyle | 'split' | 'unified' | 低——标准枚举 |
diffIndicators | 'bars' | 低 |
hunkSeparators | 'line-info' | 低 |
enableLineSelection | true | 低 |
enableHoverUtility | true | 中——已弃用的属性 |
onLineSelectionEnd | 回调 | 中——签名可能会变更 |
3. Shadow DOM 选择器(通过 unsafeCSS)
我们会针对以下选择器注入 CSS 规则。它们定位 Pierre 的 Shadow DOM 内部的 data-* 属性。如果 Pierre 重命名或移除其中任何属性,我们的样式就会在不报错的情况下失效。
当前使用:
:host——shadow root[data-diff]——根差异容器[data-file]——文件包装器[data-diffs-header]——标题栏[data-error-wrapper]——错误显示[data-virtualizer-buffer]——虚拟滚动缓冲区[data-file-info]——文件元数据行[data-column-number]——行号槽[data-diffs-header] [data-title]——标题(我们会将其隐藏)[data-diff-type='split']——分栏布局模式[data-overflow='scroll']/[data-overflow='wrap']——溢出模式
4. 覆盖的 CSS 变量
我们会覆盖这些 --diffs-* 变量,为 Pierre 设置主题:
--diffs-bg、--diffs-fg——基础颜色--diffs-dark-bg、--diffs-light-bg——特定主题的背景色--diffs-dark、--diffs-light——特定主题的前景色
5. 注入的 CSS 变量(自定义)
我们会在 Shadow DOM 外部的包装器 div 上设置这些变量,依赖 CSS 自定义属性继承:
--split-left、--split-right——控制分栏窗格的网格比例
unsafeCSS 的网格覆盖规则会引用这些变量:grid-template-columns: var(--split-left, 1fr) var(--split-right, 1fr)。1fr 回退值可确保未设置变量时布局仍然安全。
6. Grid 布局假设
Pierre 的拆分视图使用 CSS Grid,设置为 grid-template-columns: 1fr 1fr。我们会为可调整大小的拆分窗格覆盖此设置。如果 Pierre 更改其布局引擎(例如改用 flexbox 或不同的网格结构),此覆盖将不再生效。
**验证方法:**在上游源代码中搜索差异组件样式里的 grid-template-columns。
验证清单
审查涉及 Pierre 集成的更改时,请检查:
Props 和类型
- 阅读当前的
.d.ts文件,确认 prop 名称和类型未发生变化 - 检查是否仍支持
renderHoverUtility(此项已弃用,可能会被移除) - 验证
DiffLineAnnotation是否仍使用side: 'deletions' | 'additions'(而非'old' | 'new') - 确认
SelectedLineRange的结构:{ start, end, side? }
Shadow DOM 选择器
- 在上游源代码中搜索我们在
unsafeCSS中定位的每个data-*属性 - 如果升级软件包版本,对比新旧 CSS/HTML 输出,检查属性是否已重命名
- 测试
split和unified视图——选择器取决于布局
CSS 变量
- 在上游代码中搜索
--diffs-bg、--diffs-fg以及我们覆盖的其他变量 - 验证变量名称未被重命名或移除
- 检查是否仍需要
!important(Pierre 可能会更改优先级)
主题规范
- 新 UI 元素必须使用主题令牌(
bg-border、bg-primary等),而非bg-blue-500这类硬编码颜色 -
packages/ui/components/ResizeHandle.tsx中现有的ResizeHandle组件设定了视觉规范,请与其保持一致
构建和运行时
- 运行
bun run dev:review,验证差异内容在拆分和统一模式下均能正常显示 - 检查浏览器控制台中是否有 Pierre 警告(例如
parseLineType: Invalid firstChar) - 使用仅添加和仅删除的文件进行测试(Pierre 不会为这类文件渲染拆分网格)
- 如果更改 UI 代码,请记住构建顺序:
bun run --cwd apps/review build && bun run build:hook
升级 @pierre/diffs 时
- 查看上游更新日志 / 提交历史:https://github.com/pierrecomputer/pierre
- 对比新旧版本的
.d.ts文件:# Before upgrading, snapshot current types cp -r node_modules/@pierre/diffs/dist /tmp/pierre-old # After upgrading diff -r /tmp/pierre-old node_modules/@pierre/diffs/dist - 在新版本中搜索已重命名或移除的 data 属性
- 完成上述完整验证清单
- 测试可调整大小的拆分窗格——它依赖网格布局内部实现