# OpenTUI component `hunkdiff/opentui` exports reusable terminal diff components built from the same renderer as the Hunk CLI. Use `HunkDiffView` when you want a batteries-included single-file diff, or compose the lower-level primitives when you want to build your own Hunk-like review UI without Hunk's sidebar, menus, global keyboard shortcuts, or session behavior. ## Install ```bash npm i hunkdiff @opentui/core@^0.4.2 @opentui/react@^0.4.2 react ``` `hunkdiff` declares OpenTUI and React as peer dependencies, so install them in your app. ## Quick start ```tsx import { createCliRenderer } from "@opentui/core"; import { createRoot } from "@opentui/react"; import { HunkDiffView, createHunkDiffFile, parseDiffFromFile } from "hunkdiff/opentui"; const metadata = parseDiffFromFile( { cacheKey: "before", contents: "export const value = 1;\n", name: "example.ts", }, { cacheKey: "after", contents: "export const value = 2;\nexport const added = true;\n", name: "example.ts", }, { context: 3 }, true, ); const renderer = await createCliRenderer({ useAlternateScreen: true, useMouse: true, exitOnCtrlC: true, }); const root = createRoot(renderer); root.render( , ); ``` In a real app, derive `width` from your layout or `useTerminalDimensions()`. ## Convenience vs primitives ### `HunkDiffView` `HunkDiffView` renders one file and can own an OpenTUI `scrollbox`: ```tsx ``` Use it when you just want a drop-in diff viewer. ### `HunkDiffBody` `HunkDiffBody` renders only the diff body for one file. It does not create a scrollbox, file nav, keyboard shortcuts, menus, or session bridge behavior: ```tsx ``` Use it when your app owns scrolling or surrounding layout. ### `HunkDiffFileHeader` `HunkDiffFileHeader` renders Hunk's compact file label/stats header: ```tsx selectFile(file.id)} /> ``` ### `HunkReviewStream` `HunkReviewStream` renders a top-to-bottom multi-file review stream without Hunk's app shell, chrome, keybindings, or scroll owner: ```tsx { setFileId(fileId); setHunkIndex(hunkIndex); }} /> ``` Use it when you want Hunk's main review stream but your own navigation, chrome, scrolling, and keybindings. ### `HunkFileNav` `HunkFileNav` renders Hunk's file navigation list as an optional primitive. It does not render borders, outer padding, or a scrollbox; host apps own surrounding chrome and scrolling. ```tsx setFileId(nextFileId)} /> ``` ## Building file inputs The public file model is intentionally higher-level than Hunk's internal renderer rows. Row models are not exported. ```ts type HunkDiffFileInput = { id: string; metadata: FileDiffMetadata; language?: string; path?: string; previousPath?: string; patch?: string; stats?: { additions: number; deletions: number }; isBinary?: boolean; isTooLarge?: boolean; isUntracked?: boolean; statsTruncated?: boolean; }; type HunkDiffFile = Omit & { stats: { additions: number; deletions: number }; }; ``` Components accept `HunkDiffFileInput` directly. Use `createHunkDiffFile(...)` when you want a normalized `HunkDiffFile` with paths and stats filled in once: ```tsx import { createHunkDiffFile, parseDiffFromFile } from "hunkdiff/opentui"; const file = createHunkDiffFile({ id: "example", metadata: parseDiffFromFile(beforeFile, afterFile, { context: 3 }, true), path: "example.ts", language: "typescript", }); ``` ### From before/after contents Use `parseDiffFromFile(...)` when you already have the old and new file contents. ```tsx import { createHunkDiffFile, parseDiffFromFile } from "hunkdiff/opentui"; const file = createHunkDiffFile({ id: "example", metadata: parseDiffFromFile(beforeFile, afterFile, { context: 3 }, true), }); ``` ### From unified diff text Use `createHunkDiffFilesFromPatch(...)` for a quick multi-file patch path: ```tsx import { createHunkDiffFilesFromPatch } from "hunkdiff/opentui"; const files = createHunkDiffFilesFromPatch(patchText, "example:patch"); ``` If you need direct access to Pierre's parser, `parsePatchFiles(...)` is still re-exported. ## Common props | Prop | Type | Default | Notes | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------- | | `layout` | `"split" \| "stack"` | `"split"` | Chooses side-by-side or stacked rendering. | | `width` | `number` | — | Required content width in terminal columns. | | `theme` | `"graphite" \| "midnight" \| "paper" \| "ember" \| "catppuccin-latte" \| "catppuccin-frappe" \| "catppuccin-macchiato" \| "catppuccin-mocha" \| "zenburn"` | `"graphite"` | Matches Hunk's built-in themes. | | `showLineNumbers` | `boolean` | `true` | Toggles line-number columns. | | `showHunkHeaders` | `boolean` | `true` | Toggles `@@ ... @@` hunk header rows. | | `tabWidth` | `number` | `4` | Sets source-code tab stops from 1 to 16 columns. | | `showFileSeparators` | `boolean` | `true` | Toggles separator rows between files in `HunkReviewStream`. | | `wrapLines` | `boolean` | `false` | Wraps long lines instead of clipping horizontally. | | `horizontalOffset` | `number` | `0` | Scroll offset for non-wrapped code rows. | | `highlight` | `boolean` | `true` | Enables syntax highlighting. | | `selectedHunkIndex` | `number` | `0` | Highlights one hunk as the active target for single-file components. | | `scrollable` | `boolean` | `true` | `HunkDiffView` only; primitives should be wrapped in OpenTUI scrollbox when needed. | ## Other exports - `parseDiffFromFile` - `parsePatchFiles` - `FileDiffMetadata` - `createHunkDiffFile` - `createHunkDiffFilesFromPatch` - `countHunkDiffStats` - `HUNK_DIFF_THEME_NAMES` - `HunkDiffThemeName` - `HunkDiffLayout` - `HunkDiffFile` - `HunkDiffFileInput` - `HunkDiffStats` - `HunkDiffSelection` - component prop types `parseDiffFromFile`, `parsePatchFiles`, and `FileDiffMetadata` are re-exported from `@pierre/diffs` so you can build metadata without adding a second diff dependency. ## Examples - Runnable demo overview: [`examples/README.md`](../examples/README.md) - Component demos: [`examples/7-opentui-component/README.md`](../examples/7-opentui-component/README.md) The in-repo demos import from `../../src/opentui` so they run from source. Published consumers should import from `hunkdiff/opentui`.