Skip to content
pandacoderz/ui

Diff View

Unified diff with line numbers, per-hunk actions, and accepted or rejected states for agent-proposed patches.

src/components/prompt-input.tsx+42
@@ -1,8 +1,10 @@
11 export function submit(value: string) {
2 if (!value) return;
3 onSubmit?.(value);
2+ const trimmed = value.trim();
3+ if (!trimmed) return;
4+ onSubmit?.(trimmed);
45 }
56
67 export function reset() {
78 setValue("");
9+ textareaRef.current?.focus();
810 }

Installation

npx shadcn@latest add https://ui.spencerwueste.com/r/diff-view.json

Ships with lib/diff.ts, a small LCS line diff. Swap it for the diff package when you need word-level diffs or very large files.

Usage

import { DiffHeader, DiffHunkView, DiffView } from "@/components/pandacoderz-ui/diff-view";
import { computeDiff, diffStats } from "@/lib/diff";
const hunks = computeDiff(before, after);
const { additions, deletions } = diffStats(hunks);

<DiffView>
  <DiffHeader path="src/app.tsx" additions={additions} deletions={deletions} />
  {hunks.map((hunk) => (
    <DiffHunkView
      key={hunk.id}
      hunk={hunk}
      decision={decisions[hunk.id]}
      actions={<AcceptRejectButtons onDecide={(d) => decide(hunk.id, d)} />}
    />
  ))}
</DiffView>

If your API already returns hunks, skip computeDiff and pass them straight in. The shape is { id, oldStart, oldLines, newStart, newLines, lines: { type, text, oldLine?, newLine? }[] }.

API Reference

DiffHeader

Prop Type Description
path string File path shown in monospace.
additions / deletions number Renders the +/− stats.
children ReactNode Right-aligned actions.

DiffHunkView

Prop Type Description
hunk DiffHunk The hunk to render.
actions ReactNode Shown in the hunk header until a decision is made.
decision "accepted" | "rejected" Replaces actions with a badge; rejected hunks dim.
lineNumbers boolean Default true.