Diff viewer component

The component that renders a source-code diff in a code-hosting surface: split (side-by-side) and unified (inline) modes, per-file sticky header with collapse + stats, expandable context hunks, line-level add/remove/ context styling, and anchors for inline review comments. Modeled after the GitHub diff view (Primer). Used by Koder Flow PR/commit/compare surfaces.

Status: v0.1.0 — Draft. Promoted from the GitHub Primer parity scan (metadocsstack #092). Live URL once rendered: kds.koder.dev/<locale>/components/components-diff-viewer.html. Source: https://primer.style/ (diff / Blob view patterns).

R1 — Two modes

  • Unified (inline): removed and added lines interleaved in one column;

    default on narrow viewports.

  • Split (side-by-side): old on the left, new on the right; default on

    wide viewports.

  • The mode is a user toggle, persisted per user (not per file). Both

    modes render identical semantic line data (R3) — only layout differs.

R2 — Per-file block

Each changed file is its own block with a sticky header (stays pinned while scrolling that file's hunks) containing:

  • file path (old→new on rename), with directory de-emphasized;
  • change stats (+a −b) using the add/remove semantic colors;
  • a collapse/expand toggle (collapsed files render header only);
  • file-level status (added / modified / renamed / deleted / binary).

R3 — Line model

Every rendered line is exactly one of: context, added, removed, or hunk-header (the @@ … @@ separator). Add/remove use semantic fills + a leading gutter marker (+/) so the distinction never relies on color alone (R7). Old and new line numbers render in gutters (both columns in split; two gutters in unified). Syntax highlighting is delegated to specs/components/code-block.kmd (the diff layer only adds the change overlay).

R4 — Expandable context

Between hunks, an expand-context control loads the hidden unchanged lines (expand up / down / all). Expansion fetches incrementally; the expanded lines render as context. Reaching file start/end disables the respective direction.

R5 — Inline comment anchors

  • Any line exposes an affordance (on hover / keyboard focus) to start a

    review comment thread anchored to that line (and, for multi-line selection, a line range).

  • Anchored threads render inline beneath the line in both modes and are

    owned by specs/patterns/code-review.kmd (this component provides the anchor + slot, not the thread UI).

  • Anchors are stable to context expansion (anchored by line identity, not

    scroll position).

R6 — Large & non-text diffs

  • Files over a size/line threshold render collapsed by default with a

    "Load diff" action (avoid freezing on huge diffs); the threshold is a surface setting, and truncation is always disclosed (no silent cap).

  • Binary files show a "Binary file changed" notice; images show an

    old/new visual compare (side-by-side / swipe), not a text diff.

R7 — Accessibility

  • Add/remove conveyed by gutter marker + line-number column + semantic

    fill (never color alone — WCAG 1.4.1).

  • The diff is navigable by keyboard (next/prev changed hunk, next comment

    thread); line numbers are programmatically associated with their lines.

  • Screen-reader output announces line kind ("added line 42: …").

R8 — Tokens & i18n

  • All fillsmarkers from Verge tokens (`specsthemesverge.kmd`); light

    dark via the token layer.

  • Chrome strings (stats label, "Load diff", "Expand", file status,

    "Binary file changed") are translatable per specs/i18n/contract.kmd; code content itself is never translated.

Não-escopo

  • Syntax highlighting internals (specs/components/code-block.kmd).
  • The review threaddecision UI (`specspatterns/code-review.kmd`).
  • Diff computation / server-side hunking (Koder Flow backend).
  • Word-level intra-line diff (optional v1 enhancement).