Guard against breaking the @pierre/diffs integration in Plannotator's code review UI. Use this skill whenever modifying DiffViewer.tsx, upgrading the @pierre/diffs package, changing unsafeCSS injection, adding new props to FileDiff, or touching shadow DOM selectors or CSS variables that cross into Pierre's shadow boundary. Also trigger when someone asks "will this break the diff viewer", "is this safe to change", or when reviewing PRs that touch the review-editor package.
77
96%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Plannotator's code review UI wraps @pierre/diffs — an open-source diff renderer that uses Shadow DOM. The integration is concentrated in a single file but relies on undocumented internals (shadow DOM selectors, CSS variable names, grid layout assumptions). This skill helps verify changes don't break that contract.
node_modules/@pierre/diffs/dist/ (.d.ts files)packages/review-editor/components/DiffViewer.tsxpackages/review-editor/package.json for the pinned versionAlways verify against the upstream repo or local .d.ts files — don't rely on memory of the API shape.
import { FileDiff } from '@pierre/diffs/react';
import { getSingularPatch, processFile } from '@pierre/diffs';These are the only three imports. DiffViewer.tsx is the only file that touches Pierre.
FileDiff)Read the current prop types from node_modules/@pierre/diffs/dist/react/index.d.ts or the upstream source. The props we use:
| Prop | Type | Notes |
|---|---|---|
fileDiff | FileDiffMetadata | From getSingularPatch() or processFile() |
options | FileDiffOptions<T> | See options table below |
lineAnnotations | DiffLineAnnotation<T>[] | { side, lineNumber, metadata } |
selectedLines | SelectedLineRange | null | { start, end, side } |
renderAnnotation | (ann) => ReactNode | Custom inline annotation renderer |
renderHoverUtility | (getHoveredLine) => ReactNode | The + button on hover (deprecated upstream — watch for removal) |
| Option | Value We Pass | Risk |
|---|---|---|
themeType | 'dark' | 'light' | Low — standard enum |
unsafeCSS | CSS string | High — targets internal selectors |
diffStyle | 'split' | 'unified' | Low — standard enum |
diffIndicators | 'bars' | Low |
hunkSeparators | 'line-info' | Low |
enableLineSelection | true | Low |
enableHoverUtility | true | Medium — deprecated prop |
onLineSelectionEnd | callback | Medium — signature could change |
unsafeCSS)These are the selectors we inject CSS rules against. They target data-* attributes inside Pierre's shadow DOM. If Pierre renames or removes any of these, our styling breaks silently.
Currently used:
:host — shadow root[data-diff] — root diff container[data-file] — file wrapper[data-diffs-header] — header bar[data-error-wrapper] — error display[data-virtualizer-buffer] — virtual scroll buffer[data-file-info] — file metadata row[data-column-number] — line number gutter[data-diffs-header] [data-title] — title (we hide it)[data-diff-type='split'] — split layout mode[data-overflow='scroll'] / [data-overflow='wrap'] — overflow modeWe override these --diffs-* variables to theme Pierre:
--diffs-bg, --diffs-fg — base colors--diffs-dark-bg, --diffs-light-bg — theme-specific backgrounds--diffs-dark, --diffs-light — theme-specific foregroundsWe set these on a wrapper div outside the shadow DOM, relying on CSS custom property inheritance:
--split-left, --split-right — control the split pane grid ratioThe unsafeCSS grid override references these: grid-template-columns: var(--split-left, 1fr) var(--split-right, 1fr). The 1fr fallback ensures the layout is safe if the variables aren't set.
Pierre's split view uses CSS Grid with grid-template-columns: 1fr 1fr. We override this for the resizable split pane. If Pierre changes its layout engine (e.g., to flexbox or a different grid structure), the override will stop working.
How to verify: In the upstream source, search for grid-template-columns in the diff component styles.
When reviewing changes that touch the Pierre integration, check:
.d.ts files to confirm prop names and types haven't changedrenderHoverUtility is still supported (it's deprecated — may be removed)DiffLineAnnotation still uses side: 'deletions' | 'additions' (not 'old' | 'new')SelectedLineRange shape: { start, end, side? }data-* attribute we target in unsafeCSSsplit and unified views — selectors are layout-dependent--diffs-bg, --diffs-fg, and other variables we override!important is still needed (Pierre may change specificity)bg-border, bg-primary, etc.), not hardcoded colors like bg-blue-500ResizeHandle component in packages/ui/components/ResizeHandle.tsx sets the visual convention — match itbun run dev:review and verify the diff renders in both split and unified modesparseLineType: Invalid firstChar)bun run --cwd apps/review build && bun run build:hook.d.ts files between old and new versions:
# 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/dist193b07e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.