# Code Diff Viewer

Code Diff Viewer [#code-diff-viewer]

The **Code Diff Viewer** shows a line-level diff of two **local** strings — additions in green, deletions in red — in either a **unified** (single-column) or **split** (side-by-side) layout. The diff is computed in-browser with a dependency-free longest-common-subsequence algorithm. There is **no ML hook and no server**.

It is the text/code analog of the image-only `BeforeAfterImageViewer`. Use it for the before/after of:

* **PII redaction** — `redactPII()` raw → redacted.
* **Translation** — `translate()` source → target.
* **Any text transform** — prompt edits, formatting, rewrites.

**Local content only.** You pass two strings; it draws the diff. Nothing leaves the device.

**When to use it:** make a text transformation legible — show exactly what your local pipeline changed.

Preview [#preview]

```tsx
'use client';

/**
 * @file code-diff-viewer-demo.tsx
 * @description Demo for CodeDiffViewer, used by the docs live preview. Diffs a
 * realistic PII-redaction before/after (the canonical `redactPII` use case) and
 * lets you toggle unified vs split mode. Pure client-side; no model download.
 */

import { useState } from 'react';
import { CodeDiffViewer } from '@/components/code-diff-viewer';

const RAW = `Hi, I'm Jane Doe.
Email me at jane.doe@example.com
or call 555-123-4567.
My account is 4111 1111 1111 1111.`;

const REDACTED = `Hi, I'm [REDACTED_NAME].
Email me at [REDACTED_EMAIL]
or call [REDACTED_PHONE].
My account is [REDACTED_CARD].`;

export default function CodeDiffViewerDemo() {
  const [mode, setMode] = useState<'unified' | 'split'>('unified');

  return (
    <div className="flex flex-col gap-3">
      <div className="inline-flex w-fit overflow-hidden rounded-md border border-border text-sm">
        {(['unified', 'split'] as const).map((m) => (
          <button
            key={m}
            type="button"
            onClick={() => setMode(m)}
            className={
              mode === m
                ? 'bg-primary px-3 py-1 text-primary-foreground'
                : 'px-3 py-1 text-muted-foreground hover:bg-accent'
            }
          >
            {m}
          </button>
        ))}
      </div>
      <CodeDiffViewer
        original={RAW}
        modified={REDACTED}
        mode={mode}
        originalLabel="Raw"
        modifiedLabel="Redacted"
        className="max-w-xl"
      />
    </div>
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/artifacts/code-diff-viewer
```

Dependencies [#dependencies]

* **Data source:** diffs the two strings you pass — works with any backend; the diff itself needs no hook. Recommended LocalMode producers: `redactPII()` / `translate()` from `@localmode/core` to generate the before/after strings (optional).
* `clsx` + `tailwind-merge` — via the shared `cn()` util (installed automatically as a registry dependency)

> No diff library is installed — the line diff is computed in-component (LCS).

Files installed [#files-installed]

* `code-diff-viewer.tsx` — the component
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**CodeDiffViewer**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `original` | `string` | — | **Required.** The original ("before") string. |
| `modified` | `string` | — | **Required.** The modified ("after") string. |
| `mode` | `"split" \| "unified"` | `"unified"` | Display mode. |
| `originalLabel` | `string` | `"Original"` | Label for the original side (split header / unified caption). |
| `modifiedLabel` | `string` | `"Modified"` | Label for the modified side. |
| `showLineNumbers` | `boolean` | `true` | When false, line numbers are hidden. |

Examples [#examples]

PII-redaction before/after [#pii-redaction-beforeafter]

```tsx
import { redactPII } from '@localmode/core';
import { CodeDiffViewer } from '@/components/artifacts/code-diff-viewer';

export function RedactionDiff({ raw }: { raw: string }) {
  const redacted = redactPII(raw);
  return (
    <CodeDiffViewer
      original={raw}
      modified={redacted}
      originalLabel="Raw"
      modifiedLabel="Redacted"
    />
  );
}
```

Translation source/target (split view) [#translation-sourcetarget-split-view]

```tsx
<CodeDiffViewer
  original={source}
  modified={translated}
  mode="split"
  originalLabel="English"
  modifiedLabel="French"
/>
```

Unified vs split [#unified-vs-split]

```tsx
<CodeDiffViewer original={a} modified={b} mode="unified" />
<CodeDiffViewer original={a} modified={b} mode="split" />
```

Local-first boundary [#local-first-boundary]

This is a **local** diff. It takes two in-memory strings and computes the diff client-side. It needs no model, makes no network request, and runs no remote tooling.

Customization [#customization]

Additions/deletions use Tailwind's `emerald` / `red` palettes; everything else is shadcn/ui CSS-variable utilities (`bg-card`, `border-border`, `text-muted-foreground`), so it inherits your theme. Because you own the file, swap the highlight colors, hide line numbers via `showLineNumbers={false}`, or render the viewer inside an [`Artifact`](/docs/artifacts/artifact) canvas.