FD

fancy-diff

@particle-academy/fancy-diff

Human+ side-by-side document diff with hunk acceptance — a client-side, zero-dep diff engine or a git unified-diff datasource.

TSUI · 1 component1 components
$npm install @particle-academy/fancy-diff
Authoring surface
Humans compose it

Controlled value + onChange, JSON-friendly props, terse defaults — so people (and agents) build it fast.

Inhabited surface
Agents drive it over MCP

A bridge in @particle-academy/agent-integrations exposes typed tools — agents read and mutate state through stable handles, never DOM scraping.

diff_accept_hunkdiff_reject_hunk
Why
Reviewing and accepting changes is the human's job in a Human+ app — and the changes increasingly come from an agent that drafts an edit and waits for a person to confirm it. That is the trust-but-verify loop: agents propose, humans accept or reject, hunk by hunk. Every team rebuilds this surface and most ship a read-only diff with no acceptance, or bolt acceptance onto opaque DOM an embedded agent can't drive. fancy-diff closes the gap with a controlled, client-side diff viewer where a human and an agent operate the same acceptance state — no server processing, no third-party runtime deps, and no DOM scraping.
What
A React side-by-side (or inline) document diff viewer with per-hunk accept/reject and a live merged result. The diff engine — line-level LCS plus intra-line word/char segments — runs in-browser, in-house, with zero third-party runtime dependencies. Acceptance is controlled: value is a Record<hunkId, "accepted" | "rejected" | "pending"> map with onChange; getMergedResult() / onResult fold the diff and acceptance into the merged document. The source is a JSON-friendly discriminated union with three datasources: { before, after } (compute the diff in-house), { unified } (parse a git unified diff), or { diff } (a pre-built structured Diff). It composes react-fancy primitives (toolbar, buttons, badges, cards), exposes stable data-fancy-diff-hunk handles plus render-prop slots (renderHunk / renderToolbar / renderGutter), supports mode="split" | "inline" and trust-but-verify pendingMode, and emits optional AutoActivity events through @particle-academy/fancy-auto-common. Git-diff caveat: a unified diff carries only the changed hunks plus a little context, so parsed files are flagged partial and the merged result reconstructs only the lines in the diff window — feed full { before, after } documents when you need a fully merged file.
How
Install npm i @particle-academy/fancy-diff (peers: react, react-dom, @particle-academy/react-fancy) and import the stylesheet: import { FancyDiff } from "@particle-academy/fancy-diff"; import "@particle-academy/fancy-diff/styles.css";. Diff two documents: <FancyDiff source={{ before, after }} value={value} onChange={setValue} onResult={(r) => setMerged(r.text)} />. Parse a git diff instead: <FancyDiff source={{ unified }} mode="inline" />. Read the merged document imperatively via a ref — ref.current.getMergedResult().text — and turn on pendingMode so an embedded agent stages proposals a human confirms. A one-sitting MCP bridge maps diff_accept_hunk / diff_reject_hunk onto the same controlled value/onChange loop.

Components

1 component · click any tile for a full demo, source, and install snippet.

FancyDiffOpen →
src/auth.tspartial
12export function verify(token: string) {
12export function verify(token: string) {
13- return decode(token);
13+ if (!token) return null;
14+ return decode(token);
14}
15}
Controlled split / inline diff viewer; per-hunk accept/reject with a merged result, render-prop customization, and a git unified-diff datasource.