git-diff-view

repository·main·Indexed 20 days ago

https://github.com/mrwangjusttodo/git-diff-view

A high-performance diff visualization library supporting React, Vue, Solid, Svelte, and CLI environments. It features syntax highlighting, split/unified views, and custom widget support. The ecosystem includes @git-diff-view/core for Git diff hunks, @git-diff-view/file for raw file comparisons, @git-diff-view/cli for terminal rendering, and highlighting options via @git-diff-view/lowlight and @git-diff-view/shiki.

Tokens
57.4K
Snippets
192
Records
251
Agent score
71%

What's inside git-diff-view

  1. Understand Logical Lines vs Visual Rows

    main

    When working with the scroll API, it is critical to distinguish between logical lines and visual rows:

    • Logical line (line): The user-facing line index (1-based). This is the index used by most API methods.
    • Visual row (row): The actual terminal row after text wrapping. Internal scroll offsets are calculated using rows.

    A single logical line may span multiple visual rows if the content exceeds the contentWidth.

  2. Implement scrolling in DiffView

    main

    The DiffView architecture uses a flattened display sequence to manage scrolling across hunks, content, and extend lines.

    Line Semantics in DiffView: In DiffView, line refers to the 1-based index in the flattened display sequence:

    1. Hunk line: 1 visual row (appears when mapIndex !== 0 and the hunk is visible).
    2. Content line: 1 or more visual rows (depending on wrapping).
    3. Extend line: diffViewExtendLineHeight visual rows (used when renderExtendLine and extendData are present).

    Note: The first content block does not have a leading hunk line.

    Rendering Behavior:

    • When height is set: buildDiffViewScrollLayout calculates row offsets. useScrollView and getVisibleDiffScrollLines determine the visible entries (with optional clip via ScrollSlice). DiffDisplayList renders only these visible entries.
    • When height is unset: buildDiffViewScrollLayout is skipped, and DiffDisplayList iterates through all entries via iterateDiffDisplayEntries for a full render.
  3. Compare @git-diff-view/shiki and @git-diff-view/lowlight

    main

    Choose between the Shiki highlighter and the default Lowlight highlighter based on your requirements:

    Feature@git-diff-view/lowlight@git-diff-view/shiki
    Default✅ Built-in❌ Separate package
    SetupNo setup neededAsync initialization
    AccuracyGoodExcellent (VSCode-level)
    Themeshighlight.js themesVSCode themes
    Bundle SizeSmallerLarger
    PerformanceSynchronousAsync

    Use @git-diff-view/shiki when you need:

    • VSCode-quality syntax highlighting
    • Accurate language grammar
    • VSCode-compatible themes
    • Better color fidelity

    Use @git-diff-view/lowlight (default) when:

    • You want zero configuration
    • Bundle size is critical
    • Synchronous initialization is preferred
    • Basic highlighting is sufficient
  4. How the Scroll API works

    main

    Both CodeView and DiffView support a fixed-height viewport with programmatic scrolling. To enable scroll mode, you must provide a height prop (representing the number of visual rows after line wrapping).

    Behavior

    • Without height: The full content is rendered (backward compatible).
    • With height: The viewport is constrained. If height is larger than the content, the viewport shrinks to the content height.
    • Width changes: When width changes, the scroll offset resets to 0 because line wrapping counts change.

    ScrollState Object

    onScrollChange provides a ScrollState object containing:

    • totalLines: logical line count
    • totalRows: visual row count after wrap
    • viewportHeight: effective height (min of height and totalRows)
    • scrollOffset: top visual row (0-based)
    • startLine: first visible logical line
    • endLine: last visible logical line
    • canScrollUp: boolean
    • canScrollDown: boolean

    ScrollViewRef Methods

    Use a ref to control the view programmatically:

    • getScrollState(): Get current snapshot.
    • scrollToTop(line): Align target logical line to viewport top.
    • scrollToBottom(line): Align target logical line to viewport bottom.
    • scrollUp({ unit?, step? }): Scroll up (unit is "visual" or "logical").
    • scrollDown({ unit?, step? }): Scroll down.
    import { useRef } from "react";
    import { CodeView, DiffView, type CodeViewRef } from "@git-diff-view/cli";
    
    const ref = useRef<CodeViewRef>(null);
    
    <CodeView
      ref={ref}
      file={file}
      height={20}
      width={80}
      onScrollChange={(state) => console.log(state.startLine, state.endLine)}
    />;
    
    ref.current?.scrollToTop(100);
    ref.current?.scrollDown({ unit: "logical" });
  5. Worker/Server Pattern for Performance

    main

    To avoid blocking the main thread, you can process diffs in a Web Worker or on a Node.js server. Use getBundle() to export the processed data and DiffFile.createInstance(data, bundle) on the client side to reconstruct the instance for UI components.

    // Worker/Server side - generate bundle
    const file = new DiffFile(/* ... */);
    file.initTheme('dark');
    file.init();
    file.buildSplitDiffLines();
    file.buildUnifiedDiffLines();
    
    const bundle = file.getBundle();
    // Send bundle to main thread/client
    
    // Main thread/Client side - reconstruct
    import { DiffFile } from "@git-diff-view/core";
    
    const mergedFile = DiffFile.createInstance(data, bundle);
    
    // Use with UI components
    <DiffView diffFile={mergedFile} />
  6. Implement scrolling in CodeView

    main

    In CodeView, the line refers to the source file line number (File.rawFile, ranging from 1 to rawLength).

    Rendering Behavior:

    • When height is set: The component builds a full ScrollLayout using useMemo. The useScrollView hook maintains the scrollOffset and slices the layout.rows. The final output is a single sliced ANSI <Text> component rendered inside a <Box height={height} overflow="hidden">.
    • When height is unset: All rows are rendered without slicing.
  7. Configure Styling for DiffView

    main

    You can choose between two CSS import options depending on your project's styling setup:

    • Default styles: Includes Tailwind dependencies. Use this if your project uses Tailwind. import "@git-diff-view/react/styles/diff-view.css";
    • Pure CSS: Contains no Tailwind dependencies, preventing style conflicts in non-Tailwind projects. import "@git-diff-view/react/styles/diff-view-pure.css";
    // Default styles with Tailwind
    import "@git-diff-view/react/styles/diff-view.css";
    
    // Pure CSS (no Tailwind conflicts)
    import "@git-diff-view/react/styles/diff-view-pure.css";