$npx -y skills add backnotprop/plannotator --skill pierre-guardGuard 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 variabl
| 1 | # Pierre Integration Guard |
| 2 | |
| 3 | 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. |
| 4 | |
| 5 | ## Source of Truth |
| 6 | |
| 7 | - **Upstream repo**: https://github.com/pierrecomputer/pierre/tree/main/packages/diffs |
| 8 | - **Local types**: `node_modules/@pierre/diffs/dist/` (`.d.ts` files) |
| 9 | - **Integration point**: `packages/review-editor/components/DiffViewer.tsx` |
| 10 | - **Current version**: check `packages/review-editor/package.json` for the pinned version |
| 11 | |
| 12 | Always verify against the upstream repo or local `.d.ts` files — don't rely on memory of the API shape. |
| 13 | |
| 14 | ## What We Import |
| 15 | |
| 16 | ```typescript |
| 17 | import { FileDiff } from '@pierre/diffs/react'; |
| 18 | import { getSingularPatch, processFile } from '@pierre/diffs'; |
| 19 | ``` |
| 20 | |
| 21 | These are the only three imports. `DiffViewer.tsx` is the only file that touches Pierre. |
| 22 | |
| 23 | ## API Surface to Guard |
| 24 | |
| 25 | ### 1. Component Props (`FileDiff`) |
| 26 | |
| 27 | Read the current prop types from `node_modules/@pierre/diffs/dist/react/index.d.ts` or the upstream source. The props we use: |
| 28 | |
| 29 | | Prop | Type | Notes | |
| 30 | |------|------|-------| |
| 31 | | `fileDiff` | `FileDiffMetadata` | From `getSingularPatch()` or `processFile()` | |
| 32 | | `options` | `FileDiffOptions<T>` | See options table below | |
| 33 | | `lineAnnotations` | `DiffLineAnnotation<T>[]` | `{ side, lineNumber, metadata }` | |
| 34 | | `selectedLines` | `SelectedLineRange \| null` | `{ start, end, side }` | |
| 35 | | `renderAnnotation` | `(ann) => ReactNode` | Custom inline annotation renderer | |
| 36 | | `renderHoverUtility` | `(getHoveredLine) => ReactNode` | The `+` button on hover (deprecated upstream — watch for removal) | |
| 37 | |
| 38 | ### 2. Options Object |
| 39 | |
| 40 | | Option | Value We Pass | Risk | |
| 41 | |--------|--------------|------| |
| 42 | | `themeType` | `'dark' \| 'light'` | Low — standard enum | |
| 43 | | `unsafeCSS` | CSS string | **High** — targets internal selectors | |
| 44 | | `diffStyle` | `'split' \| 'unified'` | Low — standard enum | |
| 45 | | `diffIndicators` | `'bars'` | Low | |
| 46 | | `hunkSeparators` | `'line-info'` | Low | |
| 47 | | `enableLineSelection` | `true` | Low | |
| 48 | | `enableHoverUtility` | `true` | Medium — deprecated prop | |
| 49 | | `onLineSelectionEnd` | callback | Medium — signature could change | |
| 50 | |
| 51 | ### 3. Shadow DOM Selectors (via `unsafeCSS`) |
| 52 | |
| 53 | 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. |
| 54 | |
| 55 | **Currently used:** |
| 56 | - `:host` — shadow root |
| 57 | - `[data-diff]` — root diff container |
| 58 | - `[data-file]` — file wrapper |
| 59 | - `[data-diffs-header]` — header bar |
| 60 | - `[data-error-wrapper]` — error display |
| 61 | - `[data-virtualizer-buffer]` — virtual scroll buffer |
| 62 | - `[data-file-info]` — file metadata row |
| 63 | - `[data-column-number]` — line number gutter |
| 64 | - `[data-diffs-header] [data-title]` — title (we hide it) |
| 65 | - `[data-diff-type='split']` — split layout mode |
| 66 | - `[data-overflow='scroll']` / `[data-overflow='wrap']` — overflow mode |
| 67 | |
| 68 | ### 4. CSS Variables We Override |
| 69 | |
| 70 | We override these `--diffs-*` variables to theme Pierre: |
| 71 | |
| 72 | - `--diffs-bg`, `--diffs-fg` — base colors |
| 73 | - `--diffs-dark-bg`, `--diffs-light-bg` — theme-specific backgrounds |
| 74 | - `--diffs-dark`, `--diffs-light` — theme-specific foregrounds |
| 75 | |
| 76 | ### 5. CSS Variables We Inject (Custom) |
| 77 | |
| 78 | We set these on a wrapper div outside the shadow DOM, relying on CSS custom property inheritance: |
| 79 | |
| 80 | - `--split-left`, `--split-right` — control the split pane grid ratio |
| 81 | |
| 82 | The `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. |
| 83 | |
| 84 | ### 6. Grid Layout Assumption |
| 85 | |
| 86 | 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. |
| 87 | |
| 88 | **How to verify:** In the upstream source, search for `grid-template-columns` in the diff component styles. |
| 89 | |
| 90 | ## Verification Checklist |
| 91 | |
| 92 | When reviewing changes that touch the Pierre integration, check: |
| 93 | |
| 94 | ### Props & Types |
| 95 | - [ ] Read the current `.d.ts` files to confirm prop names and types haven't changed |
| 96 | - [ ] Check if `renderHoverUtility` is still supported (it's deprecated — may be removed) |
| 97 | - [ ] Verify `DiffLineAnnotation` still uses `side: 'deletions' | 'addit |