feat(PLA-105): Vendor chapter UI components from Stage monorepo#6
Conversation
Copies the diff-rendering components needed for chapter playback into web/src/components/chapter/, replacing the monorepo's useChapterContext / useFocusedLines coupling with explicit prop callbacks. text-selection- popup is a no-op stub for V1, and pierre-diff-viewer drops the comment- form/oRPC plumbing per PLA-105. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a new "Chapter" UI module for code diff visualization, featuring a PierreDiffViewer that wraps @pierre/diffs and includes an interactive LineHighlightOverlay for highlighting key changes. It also adds a FileHeader component, UI primitives for checkboxes and tooltips, and various utilities for theme detection and DOM line targeting. Feedback focuses on moving runtime libraries to the main dependencies list, fixing a potential bug where referential equality on LineRef objects could cause UI flickering, removing dead code related to hover logic, and correcting a hydration mismatch in the useIsMac hook.
* Match prior overlay measurements by LineRef coordinates instead of object identity, so parents that recreate refs across renders don't flicker the highlight boxes. * Drop the dead isHoveringRef guard from pierre-diff-viewer since the hover utility is disabled. * Default useIsMac SSR snapshot to false to avoid a hydration mismatch on non-Mac systems. * Fix the fixture patch hunk header so its line counts match the body. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The LineHighlightOverlay references --hunk-highlight-bg / -border / -focused-bg / -focused-border for its key-change boxes, and FileHeader sticks via --content-top. Define defaults in :root so the overlay is visible and the file header sticks correctly out of the box. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a leading or trailing hunk has additionCount === 0 (fully-deleted files, pure deletion hunks) the previous math returned an inverted range whose end preceded its start. Return null instead so consumers like getKeyChangeFileLineRange skip the file rather than producing an invalid SelectedLineRange. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Flip the key-change props on PierreDiffViewer and LineHighlightOverlay from a Set + toggle callback to predicate + mark/unmark, matching the useViewState API spec'd for PLA-106 so the hook wires up directly. Drop onLineSelected — V1 has no comment plumbing to consume the selection. Vendor useLocalStorage / useDiffSettings / syntax-themes from the Stage monorepo and consume the settings hook from PierreDiffViewer so users can toggle viewMode, indicators, line-diff type, backgrounds, wrap, line numbers, and syntax theme; preferences persist in localStorage. The fixture App.tsx is wrapped with DiffSettingsProvider. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 92f37db. Configure here.
Vendored verbatim from the Stage monorepo, which uses tabs; reformat to match the rest of web/src so Biome doesn't drift on the next format. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Summary
pierre-diff-viewer,hunk-highlight-overlay,file-header,text-selection-popup) from the Stage monorepo intoweb/src/components/chapter/, replacinguseChapterContext/useFocusedLineswith explicit prop callbacks (PLA-105).@stage/typesand@stage/ui(diff-types.ts,file-status.ts,use-is-mac.ts) and adds shadcn-vendoredCheckbox/Tooltipprimitives so nothing imports from monorepo paths or oRPC.@pierre/diffs,@radix-ui/react-checkbox, and@radix-ui/react-tooltiptopackage.json, simplifiespierre-diff-viewerto a render-layer-only component (no comment form, no diff-settings/theme contexts), and stubstext-selection-popupas a no-op for V1.App.tsxwith a hand-crafted fixture that supplies key-change line refs and viewed/focus state viauseState, exercising the chapter components end-to-end.Test plan
npx tsc --noEmit -p web/tsconfig.json— cleannpm run lint— cleannpm run buildandnpm run build:web— succeednpm test— 12/12 passnpm run dev:weband loadhttp://localhost:5173— fixture renders with diff, key-change highlight, and viewed-state checkbox🤖 Generated with Claude Code