Overview
Reference usage
Lightweight unified-patch viewer for source-code and operational diffs with file headers, hunk headers, line state styling, and bounded rendering.
See the editor/Cinder surface boundary. SourceDiffViewer owns dependency-light source patches; use the editor DiffViewer for Markdown review and editor-runtime workflows.
Usage
Use SourceDiffViewer for source-code patches, agent workspace patches, Git output, and other operational unified diffs. Use DiffViewer from @lostgradient/editor for Markdown document review, front-matter handling, Markdown normalization, and prose-oriented word-level changes.
Annotation hooks
SourceDiffViewer exposes optional, dependency-light selection and file hooks so a caller can build comment/annotation UI without SourceDiffViewer ever importing @lostgradient/editor or Markdown/Milkdown/ProseMirror. These hooks are the Cinder half of the diff-review contract; the Editor package's DiffReview/DiffReviewComments consume them, but this component itself has no idea either exists.
onFilesChange(files)reports a descriptor — occurrence, paths, label, hunk/changed-line counts, and whether the file's content was entirely cut bymaxLines— for every file the parser recognized, including a fully truncated one that never appears in the rendered output.activeFileOccurrencerenders exactly one recognized file; omit it (the default) to render every file, matching prior behavior. A fully truncated active file still renders its header and an explicit "Lines unavailable at the current display limit." message.fileAnnotationrenders alongside each file's header regardless of the other annotation hooks, receiving that file's descriptor.lineAnnotationrenders next to each commentable row/side's add-comment control, so — like the control itself — it only appears whenonAnnotationSelectionChangeis also supplied.annotationSelection+onAnnotationSelectionChangeare a controlled pair, separate from ordinary diff navigation: a line's add-comment button or Shift-click selects/extends and commits immediately; a focused control's Enter starts a keyboard selection, Shift+Arrow extends it, Escape cancels and returns focus to the origin, and Enter commits. An extension that would cross a file, hunk, or side boundary is rejected — the origin never moves — with the reason announced in an instance-scoped status region.refexposesfocusFile(fileOccurrence)andfocusAnchor(anchor), each returning{ status: 'focused' | 'unavailable' }and operating only within that component instance.
Every hook is optional and additive: with none supplied, rendering is byte-for-byte the same as before this feature existed, and two instances on the same page never share selection state, focus, or DOM ids.
Contract note: this repository's check-prop-conventions gate bans a non-native-passthrough lowercase on* prop, so the cross-package diff-review contract's onfileschange/onannotationselectionchange spelling is translated here to onFilesChange/onAnnotationSelectionChange. Every field name and payload shape is otherwise exactly as specified.
Guidance
Use When
- Rendering source-code unified patches from agent output, Git operations, workspace changes, or review systems.
- Showing operational patch output where file and hunk structure matters more than Markdown front matter or word-level prose review.
Avoid When
- Comparing two Markdown documents with normalization, front-matter, and revert affordances — use
DiffViewerfrom@lostgradient/editor. - Showing syntax-highlighted code samples rather than patch output — use code-block instead.
When to use
- Rendering source-code unified patches from agent output, Git operations, workspace changes, or review systems.
- Showing operational patch output where file and hunk structure matters more than Markdown front matter or word-level prose review.
- Comparing two Markdown documents with normalization, front-matter, and revert affordances — use `DiffViewer` from `@lostgradient/editor`.
- Showing syntax-highlighted code samples rather than patch output. Use Code Block instead
Examples
Basic source diff viewer
Unified patch output with file headers, hunk headers, and source line states.
Bounded source diff viewer
Large patches can cap rendered diff rows while preserving the total count.
Props
Name | Type | Default | Description |
|---|---|---|---|
patch required | text | Unified patch text to parse and render. | |
ariaLabel | text | Accessible label for the diff region. | |
maxLines | number | 1000 | Maximum number of diff rows to render before truncating. |
lineNumbers | boolean | true | Whether old and new line-number gutters are rendered. |
empty | snippet | Rendered when the patch is empty or contains no displayable diff rows.
Falls back to a default "No patch lines to display." message — matching
the empty snippet the chart/command families expose. | |
onFilesChange | (files: SourceDiffFileDescriptor[]) => void | Called with a descriptor for every recognized file whenever the parsed
patch changes. Named onFilesChange (camelCase) rather than the
lowercase onfileschange the cross-package diff-review contract uses
elsewhere: this repository's check-prop-conventions gate structurally
bans a non-native-passthrough lowercase on* prop, so the callback
casing is translated to this repository's house style while every field
name and the payload shape stay exactly as specified. | |
activeFileOccurrence | number | Controlled zero-based file occurrence to render exclusively. Absent (the default) renders every file, matching prior behavior. | |
fileAnnotation | snippet | Rendered next to each file's header, alongside its file-level annotation controls. | |
lineAnnotation | snippet | Rendered next to each commentable row's add-comment control. Like that
control, it only appears when onAnnotationSelectionChange is also
supplied — pass both to render per-line annotation markers. | |
annotationSelection | {
fileOccurrence: number;
hunkOccurrence: number;
side: SourceDiffAnnotationSide;
/** Inclusive, one-based. */
startLine: number;
/** Inclusive, one… Show full type{
fileOccurrence: number;
hunkOccurrence: number;
side: SourceDiffAnnotationSide;
/** Inclusive, one-based. */
startLine: number;
/** Inclusive, one-based. */
endLine: number;
/** Always `'raw-source'`: source patches never claim normalized coordinates. */
coordinateSpace: 'raw-source';
selectedText: string;
/** Up to three same-side lines immediately before `startLine`, in document order. */
contextBefore: string[];
/** Up to three same-side lines immediately after `endLine`, in document order. */
contextAfter: string[];
oldPath: string | null;
newPath: string | null;
} | Controlled current annotation selection. Distinct from ordinary diff navigation: setting or clearing it never affects which rows are shown. | |
onAnnotationSelectionChange | (selection: SourceDiffAnnotationSelection | null) => void | Called when the user commits, extends, or clears an annotation
selection. See onFilesChange above for why this repository spells the
contract's onannotationselectionchange as camelCase. | |
ref bindable | {
/** Focuses the labeled header of the given file occurrence, if rendered. */
focusFile: (fileOccurrence: number) => SourceDiffViewerFocusResult;
/** Foc… Show full type{
/** Focuses the labeled header of the given file occurrence, if rendered. */
focusFile: (fileOccurrence: number) => SourceDiffViewerFocusResult;
/** Focuses the line control for the given anchor, if its row is rendered. */
focusAnchor: (
anchor: Pick<SourceDiffAnnotationSelection, 'fileOccurrence' | 'hunkOccurrence' | 'side'> & {
startLine: number;
},
) => SourceDiffViewerFocusResult;
} | Programmatic handle for focusFile/focusAnchor. |