Skip to content

feat(opentui): publish reusable diff component#182

Merged
benvinegar merged 4 commits intomainfrom
feat/opentui-diff-component
Apr 8, 2026
Merged

feat(opentui): publish reusable diff component#182
benvinegar merged 4 commits intomainfrom
feat/opentui-diff-component

Conversation

@benvinegar
Copy link
Copy Markdown
Member

Summary

  • publish HunkDiffView as a typed hunkdiff / hunkdiff/opentui entrypoint for OpenTUI apps
  • keep the exported renderer focused on a reusable single-file diff body while preserving the existing CLI package
  • update npm packaging, pack checks, docs, and diff highlight caching for patch-less library inputs

Testing

  • bun run typecheck
  • bun test src/opentui/HunkDiffView.test.tsx
  • bun run build:npm
  • bun run check:pack
  • bun run lint
  • bun run format:check

Expose Hunk's single-file diff view as a typed npm entrypoint so other OpenTUI apps can render diffs with the same terminal-native UI.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 8, 2026

Greptile Summary

This PR publishes a new reusable HunkDiffView OpenTUI React component as a library entrypoint (hunkdiff / hunkdiff/opentui), adds a metadataFingerprint fallback for highlight caching when no patch text is available, and wires up the build pipeline, TypeScript declarations, and pack checks for the new output.

Key changes:

  • New src/opentui/ module with HunkDiffView, public types, theme exports, and re-exported @pierre/diffs helpers
  • package.json gains exports and peerDependencies fields; build-npm.sh gains a second Bun + tsc pass for the library bundle
  • useHighlightedDiff.ts now falls back to a metadata-based fingerprint when patch is empty string
  • One focused integration test for the public OpenTUI entrypoint

Issues found:

  • @opentui/core, @opentui/react, and react appear in both dependencies and peerDependencies, which can cause duplicate React installations for library consumers
  • metadataFingerprint serializes the full hunks structure on every render for patch-less diffs
  • tsconfig.opentui.json emits declarations for all transitively imported internal files, leaking internal type shapes into the published package

Confidence Score: 4/5

Safe to merge after resolving the peerDeps/dependencies duplication, which risks broken React hooks for library consumers

One P1 finding (React and OpenTUI listed in both dependencies and peerDependencies) can cause dual React installations that break hooks for anyone consuming the library component. The remaining findings are P2 style/quality concerns that don't block functionality. The core component logic, build pipeline, and test coverage are well-structured.

package.json — the dependencies / peerDependencies overlap needs fixing before this is published

Vulnerabilities

No security concerns identified. The new code processes caller-supplied diff metadata and renders it into terminal UI — no network calls, no shell execution, no credential handling, and no user-supplied content reaches a SQL or HTML sink.

Important Files Changed

Filename Overview
package.json Adds exports map and peerDependencies; @opentui/core, @opentui/react, and react are duplicated across both dependencies and peerDependencies, which can cause duplicate React instances for library consumers
src/ui/diff/useHighlightedDiff.ts Adds metadataFingerprint fallback for zero-length patch; works correctly, but JSON-serializes the full hunks structure on every render cycle for patch-less files
src/opentui/HunkDiffView.tsx Clean new component: adapts the public diff shape to the internal model, wires up highlighting + row building, and handles empty/no-diff states correctly
tsconfig.opentui.json Correct for generating declarations, but emitDeclarationOnly with transitive imports will emit .d.ts files for all internal src/ui/ and src/core/ files into the published package
src/opentui/HunkDiffView.test.tsx Well-structured integration test using the public entrypoint; covers rendering and theme-name export contract
scripts/build-npm.sh Adds a second Bun build pass for the OpenTUI library bundle with correct externals, followed by tsc for declarations
src/opentui/index.ts Clean barrel re-exporting the public API surface: component, types, theme names, and @pierre/diffs helpers

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Consumer: import { HunkDiffView } from 'hunkdiff'"] --> B["src/opentui/index.ts"]
    B --> C["HunkDiffView component"]
    B --> D["Re-exports: parseDiffFromFile\nparsePatchFiles\nFileDiffMetadata"]
    D --> E["@pierre/diffs"]
    C --> F["toInternalDiffFile()\nAdapts HunkDiffFile → DiffFile"]
    F --> G["useHighlightedDiff()"]
    G --> H{"patch.length === 0?"}
    H -- "yes (library path)" --> I["metadataFingerprint()\nJSON.stringify hunks"]
    H -- "no (CLI path)" --> J["patchFingerprint()\nSubstring-based hash"]
    I --> K["buildCacheKey → highlight cache"]
    J --> K
    K --> L["loadHighlightedDiff()"]
    L --> M["buildSplitRows / buildStackRows"]
    M --> N["DiffRowView → scrollbox/box"]
    N --> O["OpenTUI terminal render"]
Loading

Reviews (1): Last reviewed commit: "feat(opentui): publish reusable diff com..." | Re-trigger Greptile

Show how to embed HunkDiffView from before/after contents and from raw patch text so OpenTUI consumers have concrete starting points.
@tanvesh01
Copy link
Copy Markdown
Contributor

oooh nice! was gonna request this!

Let OpenTUI consumers switch between split and stack layouts directly in the runnable example, and fix the header rows so the controls render cleanly in narrow terminals.
Avoid duplicate React/OpenTUI installs for consumers, keep the public import path explicit, replace the expensive patch-less highlight fingerprint, and publish only the OpenTUI declaration files.
@benvinegar benvinegar merged commit dc46f0e into main Apr 8, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants