Skip to content

Latest commit

 

History

History
134 lines (103 loc) · 4.92 KB

File metadata and controls

134 lines (103 loc) · 4.92 KB

OpenTUI component

dunkdiff/opentui exports DunkDiffView, a reusable terminal diff component built from the same renderer as the dunk CLI.

Use it when you want dunk's split or stack diff view inside your own OpenTUI app.

Install

npm i dunkdiff @opentui/core @opentui/react react

dunk declares OpenTUI and React as peer dependencies, so install them in your app.

Quick start

import { createCliRenderer } from "@opentui/core";
import { createRoot } from "@opentui/react";
import { DunkDiffView, parseDiffFromFile } from "dunkdiff/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(
  <DunkDiffView
    diff={{
      id: "example",
      metadata,
      language: "typescript",
      path: "example.ts",
    }}
    layout="split"
    width={88}
    theme="midnight"
  />,
);

In a real app, derive width from your layout or useTerminalDimensions().

Building the diff input

DunkDiffView renders one file at a time. Pass a diff object shaped like this:

type DunkDiffFile = {
  id: string;
  metadata: FileDiffMetadata;
  language?: string;
  path?: string;
  patch?: string;
};

From before/after contents

Use parseDiffFromFile(...) when you already have the old and new file contents.

import { parseDiffFromFile } from "dunkdiff/opentui";

const metadata = parseDiffFromFile(beforeFile, afterFile, { context: 3 }, true);

From unified diff text

Use parsePatchFiles(...) when you already have a patch string.

import { parsePatchFiles } from "dunkdiff/opentui";

const parsed = parsePatchFiles(patchText, "example:patch", true);
const metadata = parsed.flatMap((entry) => entry.files)[0];

if (!metadata) {
  throw new Error("Expected at least one diff file.");
}

Props

Prop Type Default Notes
diff DunkDiffFile undefined File to render. When omitted, the component shows an empty-state message.
layout "split" | "stack" "split" Chooses side-by-side or stacked rendering.
width number Required content width in terminal columns.
theme "graphite" | "midnight" | "paper" | "ember" "graphite" Matches dunk's built-in themes.
showLineNumbers boolean true Toggles line-number columns.
showHunkHeaders boolean true Toggles @@ ... @@ hunk header rows.
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.
scrollable boolean true Set to false if your parent view owns scrolling.
selectedHunkIndex number 0 Highlights one hunk as the active target.

Other exports

  • parseDiffFromFile
  • parsePatchFiles
  • FileDiffMetadata
  • DUNK_DIFF_THEME_NAMES
  • DunkDiffThemeName
  • DunkDiffLayout
  • DunkDiffFile
  • DunkDiffViewProps

parseDiffFromFile, parsePatchFiles, and FileDiffMetadata are re-exported from @pierre/diffs so you can build metadata without adding a second diff dependency.

Examples

The in-repo demos import from ../../src/opentui so they run from source. Published consumers should import from dunkdiff/opentui.