Skip to content

Migrate the diff renderer to @pierre/diffs - #110

Open
baanish wants to merge 3 commits into
claude-design-rebuildfrom
pierre-diffs-migration
Open

Migrate the diff renderer to @pierre/diffs#110
baanish wants to merge 3 commits into
claude-design-rebuildfrom
pierre-diffs-migration

Conversation

@baanish

@baanish baanish commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Stacked on #109. Implements the diff-renderer decision from the owner's design threads: the old @git-diff-view/react output "is worthless. just use diffs.com".

What

  • Diff rendering is handed to @pierre/diffs (diffs.com's library, Apache-2.0): PatchDiff per parsed patch file inside the existing multi-file shell, MultiFileDiff for before/after contents. Shiki-based highlighting, shadow-DOM styles, split/unified built in.
  • Fixes a real bug for free: the oldContent/newContent path previously rendered an empty diff body (the old library never computed diffs from raw contents); MultiFileDiff does, and a unit test now covers the path.
  • The entire vendored-stylesheet pipeline is deleted: the mirrored public/vendor/diff-view-pure.css(.br), its loader module, _headers entry, assets:compress mirror step, and the byte-sync test. Net diff is −998 lines.
  • Theming stays on the bench-instrument contract: diff bodies dark charcoal in both themes, wired through --diffs-* custom properties (names verified against the package) on .diff-renderer-frame, with the pinned pierre-dark Shiki theme.
  • The library is imported only through a small use client bridge (src/lib/diff/pierre-react.ts); a direct deep import from the deferred chunk produced webpack chunk-id corruption in a prior migration attempt, and the bridge is also the seam unit tests mock.
  • Preserved unchanged: patch parse gate and parseGitPatchBundle metadata, per-file nav and headers, binary short-circuit, unified/split toggle with the phone gating, raw-patch fallback and error boundary, every data-* attribute, and the next/dynamic code-split boundary.

Budgets

The deferred diff chunk now measures 142 KiB gzipped vs 323.9 KiB before; the budget is re-keyed to the artifact-stage dynamic import and tightened from 340 to 160 KiB. Shiki grammars load as separate per-language on-demand chunks outside this key.

Verification

  • Lint 0 errors, typecheck clean, 321/321 unit tests
  • Static export builds; all four budgets pass (homepage 111.7/115 KiB)
  • Chromium e2e suite 28/28, including a rewritten shadow-DOM assertion replacing the old stylesheet-lifecycle test
  • Self-hosted CSP already allows style-src 'unsafe-inline', so shadow-DOM styles are unaffected
  • Visual snapshots deliberately not regenerated (same policy as Rebuild UI in the bench-instrument design language #109)

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 09ffbf0b-3c16-41fe-8d14-019eaddf0d61

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​pierre/​diffs@​1.3.58010099100100

View full report

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 7, 2026

Copy link
Copy Markdown

Deploying agent-render with  Cloudflare Pages  Cloudflare Pages

Latest commit: 290209a
Status: ✅  Deploy successful!
Preview URL: https://a1764e7c.agent-render.pages.dev
Branch Preview URL: https://pierre-diffs-migration.agent-render.pages.dev

View logs

@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown

Greptile Summary

The PR migrates rich diff rendering from @git-diff-view/react to @pierre/diffs and removes the former vendored stylesheet pipeline.

  • Uses PatchDiff for parsed patch files and MultiFileDiff for before/after content.
  • Replaces external diff CSS assets with shadow-DOM styling and inherited custom properties.
  • Updates build budgets, documentation, headers, and renderer tests for the new dependency.

Confidence Score: 4/5

The PR is not yet safe to merge because before/after content diffs remain confined to the narrow desktop navigation column.

The rich-content branch still renders .patch-bundle-files as the sole child of a two-column grid, causing automatic placement in the 210–260px first column and preserving the previously reported broken desktop layout.

Files Needing Attention: src/components/renderers/diff-renderer.tsx, src/app/globals.css

Important Files Changed

Filename Overview
src/components/renderers/diff-renderer.tsx Migrates patch and content rendering to Pierre components, but the previously reported desktop content-layout defect remains.
src/app/globals.css Replaces legacy diff styling with Pierre custom properties while retaining the two-column shell responsible for the outstanding content-width issue.
src/lib/diff/pierre-react.ts Adds a client-side bridge and registers the custom CSS-variable syntax theme.
package.json Replaces @git-diff-view/react with @pierre/diffs.
scripts/check-build-budgets.mjs Re-keys and tightens the deferred diff-renderer bundle budget.

Reviews (4): Last reviewed commit: "Drop the diff toolbar's self-narrating l..." | Re-trigger Greptile

Comment thread src/components/renderers/diff-renderer.tsx
const { diffFiles } = renderedDiff;
const RichDiffView = diffLibrary?.DiffView;
const richDiffMode = diffLibrary ? getDiffLibraryMode(mode, diffLibrary) : null;
const diffOptions = getDiffOptions(mode);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: getDiffOptions(mode) builds a fresh options object on every render, and it is passed by reference straight into the heaviest deferred renderer (PatchDiff/MultiFileDiff).

DiffRendererContent re-renders on any local state change — setIsReady(true) in the rAF effect, setIsNarrowScreen media-query updates, and activeFileId selection — so every one of those re-renders hands the Shiki/shadow-DOM diff a brand-new options object identity. In the previous implementation the expensive diff work was computed once per artifact (via the warmed DiffFile instances) and was only rebuilt on artifact change; here the heavy component is re-rendered on unrelated state churn whenever the library does not memoize on its own. If @pierre/diffs re-runs diff computation/highlighting on a changed options reference, this is a per-interaction cost regression on exactly the component the PR is trying to keep off the hot path.

Memoize the options on mode (and keep it out of every-render recomputation):

Suggested change
const diffOptions = getDiffOptions(mode);
const diffOptions = useMemo(() => getDiffOptions(mode), [mode]);

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
src/components/renderers/diff-renderer.tsx 350 getDiffOptions(mode) builds a fresh options object on every render and passes it by reference into the heaviest PatchDiff/MultiFileDiff, forcing a re-render of the Shiki diff on unrelated state churn (isReady, isNarrowScreen, activeFileId). Memoize on mode.
Files Reviewed (3 source files + infra)
  • src/components/renderers/diff-renderer.tsx - 1 issue (carried forward, already commented at line 350)
  • src/lib/diff/pierre-react.ts
  • src/app/globals.css (diff-renderer-frame --diffs-* token theming)
  • package.json, scripts/check-build-budgets.mjs, scripts/compress-dictionary.mjs
  • docs and test updates for the @pierre/diffs migration

Fix these issues in Kilo Cloud

Previous Review Summaries (3 snapshots, latest commit 088fbc8)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 088fbc8)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
src/components/renderers/diff-renderer.tsx 350 getDiffOptions(mode) builds a fresh options object on every render and passes it by reference into the heaviest deferred PatchDiff/MultiFileDiff, re-rendering the Shiki diff on unrelated state churn (isReady, isNarrowScreen, activeFileId). Memoize on mode.
Files Reviewed (11 files, incremental 8428e18..HEAD)
  • .design-sync/config.json
  • .design-sync/previews/FragmentDetailsDisclosure.tsx
  • src/app/globals.css
  • src/components/renderers/code-renderer.tsx
  • src/components/renderers/diff-renderer.tsx - 1 issue (carried forward, already commented at line 350)
  • src/components/renderers/json-renderer.tsx
  • src/components/renderers/markdown-renderer.tsx
  • src/components/viewer-shell.tsx
  • src/components/viewer/artifact-stage.tsx
  • src/components/viewer/fragment-details-disclosure.tsx
  • tests/components/fragment-details-disclosure.test.tsx

Fix these issues in Kilo Cloud

Previous review (commit 8428e18)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
src/components/renderers/diff-renderer.tsx 354 getDiffOptions(mode) builds a fresh options object each render and passes it by reference into the heaviest deferred PatchDiff/MultiFileDiff, re-rendering the Shiki diff on every unrelated state change (isReady, isNarrowScreen, activeFileId). Memoize on mode.
Files Reviewed (5 files, incremental 3915683..HEAD)
  • src/app/globals.css
  • src/components/renderers/diff-renderer.tsx - 1 issue (carried forward, already commented at line 354)
  • src/components/viewer/artifact-stage.tsx
  • src/components/viewer/fragment-details-disclosure.tsx
  • src/lib/diff/pierre-react.ts

Fix these issues in Kilo Cloud

Previous review (commit 3915683)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
src/components/renderers/diff-renderer.tsx 354 getDiffOptions(mode) builds a fresh options object each render and passes it by reference into the heaviest deferred PatchDiff/MultiFileDiff, re-rendering the Shiki diff on every unrelated state change (isReady, isNarrowScreen, activeFileId). Memoize on mode.
Files Reviewed (22 files)
  • docs/architecture.md
  • docs/dependency-notes.md
  • docs/testing.md
  • package-lock.json
  • package.json
  • public/_headers
  • public/vendor/diff-view-pure.css
  • public/vendor/diff-view-pure.css.br
  • scripts/check-build-budgets.mjs - 1 issue
  • scripts/compress-dictionary.mjs
  • src/app/globals.css
  • src/components/renderers/diff-renderer.tsx - 1 issue
  • src/components/renderers/diff-view-stylesheet.ts
  • src/lib/diff/pierre-react.ts
  • tests/build-budgets.test.ts
  • tests/components/diff-renderer.test.tsx
  • tests/diff-style-asset.test.ts
  • tests/e2e/viewer.spec.ts
  • tests/headers.test.ts
  • tests/selfhosted/api-catalog.test.ts
  • tests/selfhosted/static-headers.test.ts
  • tests/serve-export-headers.test.ts

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4-flash · Input: 51.5K · Output: 21.6K · Cached: 967.7K

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 391568316d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import { Component, type CSSProperties, type ReactNode, useEffect, useMemo, useRef, useState } from "react";
import { Check, Columns2, Copy, Rows3 } from "lucide-react";
import { useResolvedTheme, type ResolvedTheme } from "@/components/theme/use-theme-controller";
import { PatchDiff, MultiFileDiff, type FileDiffProps } from "@/lib/diff/pierre-react";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the raw fallback available when the library chunk fails

When the deferred @pierre/diffs code cannot be fetched or evaluated—for example, during an interrupted load or a deployment with stale cached HTML—this static import fails before DiffRenderer or DiffRendererBoundary can mount, so the artifact stage cannot show the raw patch fallback. The previous implementation caught failure of the inner rich-renderer import and returned getFallbackState; preserve that behavior by loading the library behind a catchable boundary or supplying an error fallback for the dynamic renderer import.

Useful? React with 👍 / 👎.

@baanish
baanish force-pushed the pierre-diffs-migration branch 2 times, most recently from 8428e18 to 088fbc8 Compare August 7, 2026 06:42
baanish and others added 3 commits August 7, 2026 13:35
The old @git-diff-view/react output read as a debug dump with pseudo-table
headers; the decided direction (cursor session, criticism #15) was to hand
rendering to diffs.com's library. @pierre/diffs is Shiki-based, renders
into shadow DOM, and computes before/after diffs from raw contents, which
the old stack could not do (the oldContent/newContent path rendered an
empty diff body).

- diff-renderer.tsx: PatchDiff per parsed patch file and MultiFileDiff for
  before/after contents, imported through a small use-client bridge
  (src/lib/diff/pierre-react.ts) so the deferred chunk id stays stable
  across webpack graphs. The parse gate, per-file nav, binary handling,
  unified/split toggle, fallback, error boundary, and every data attribute
  are unchanged.
- The vendored stylesheet pipeline is gone: diff-view-stylesheet.ts,
  public/vendor/diff-view-pure.css(.br), the mirror step in
  assets:compress, its _headers entry, and the sync test. Shadow-DOM
  styles are themed via --diffs-* custom properties on
  .diff-renderer-frame, keeping diff bodies dark charcoal in both themes.
- Budget re-keyed to the artifact-stage dynamic import and tightened to
  160 KiB gzipped (measures 142 KiB; Shiki grammars load as separate
  on-demand chunks outside this key, unlike the old bundled highlighter
  at 323.9/340 KiB).
- Header tests keep exercising generic *.css.br serving via a neutral
  fixture name; the e2e stylesheet-lifecycle test now asserts shadow-DOM
  rendering with no external diff stylesheet.

Lint, typecheck, 321 unit tests, build budgets, and the chromium e2e
suite (28/28) pass. Visual snapshots are not regenerated here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The pinned pierre-dark Shiki theme highlighted diffs in a foreign color
vocabulary. Register a CSS-variables Shiki theme ("agent-render") in the
pierre bridge instead: token colors resolve from --diffs-token-* custom
properties defined on .diff-renderer-frame, which map to the app's --rb-*
rainbow palette so diff syntax matches the code renderer. The concrete
values double as baked-in fallbacks for contexts without the app CSS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The "review-style diff / syntax highlighted" meta, the fallback's "raw
patch fallback / invalid unified diff" pills, and the "Unified is the
phone default" note all narrate what the surface already shows; the
fallback's explanatory status message below the toolbar stays. Dead
.diff-mobile-note CSS removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@baanish
baanish force-pushed the pierre-diffs-migration branch from 088fbc8 to 290209a Compare August 7, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant