Domain

DiffReviewComments

Reusable, resolution-aware comments panel for a `DiffReviewState` session: every saved comment by default, with an unresolved filter, and a review note.

import { DiffReviewComments } from '@lostgradient/editor';
diff-reviewcomments
01

Overview

Reusable, resolution-aware comments panel for a DiffReviewState session (COR-509 / DR-6). Shows every saved comment by default (including resolved and outdated ones), with an explicit "Unresolved only" filter, plus the session's review note. Standalone-usable next to a bare DiffViewer/SourceDiffViewer, or composed inside the aggregate DiffReview shell.

svelte
<script lang="ts">
  import { DiffReviewComments, createDiffReviewState } from '@lostgradient/editor';

  const created = createDiffReviewState([
    {
      targetId: 'doc',
      kind: 'markdown',
      label: 'notes.md',
      original,
      current,
      normalizeInputs: false,
    },
  ]);
  let state = $state(created.ok ? created.value : undefined);
</script>

{#if state}
  <DiffReviewComments {state} onStateChange={(next) => (state = next)} />
{/if}

Props

Prop Type Description
state DiffReviewState Controlled session state (@lostgradient/editor/diff-review-state). Never mutated directly.
onStateChange (next: DiffReviewState) => void Called after every successful edit/delete/resolve/reopen/review-note action.
readonly boolean Disables every mutating control; viewing and filtering remain available. Default false.
defaultFilter 'all' | 'unresolved' Initial filter. Uncontrolled after mount. Default 'all'.
onNavigate (comment: DiffReviewComment) => void Called when a person activates a CURRENT comment's "Go to" action — never for an outdated or removed one. The host wires this to whichever diff viewer it owns, calling that viewer's own public focusAnchor/focusFile. Optional; when omitted, "Go to" on a current comment is a no-op.
class string Additional class merged onto the root <section>.

Behavior

  • Delete requires confirmation (ConfirmDialog) and is irreversible once confirmed — it removes the comment from state and from every subsequent export.
  • Edit/resolve/reopen/delete/review-note dispatch through reduceDiffReviewState (via the shared dispatchDiffReviewAction helper); a rejected action never calls onStateChange and leaves the panel's state untouched.
  • Outdated comments render an Outdated badge but are never hidden — they remain fully visible and editable/resolvable/deletable like any other comment. A comment whose target has been removed entirely (not merely changed) renders Removed instead.
  • Every comment has a "Go to" action. For a current comment this calls onNavigate; this component classifies status itself (diff-review-anchor-status.ts, shared with DiffReview) so a standalone host never has to. For an outdated/removed comment, "Go to" instead moves DOM focus to that comment's own captured-detail text (its originally captured target label and path) and never calls onNavigate — the diff is never focused for a stale or gone anchor.

Why onStateChange, not onstatechange

The ratified cross-package diff-review contract names this callback onstatechange. This repository's check-prop-conventions gate structurally bans a non-native-passthrough lowercase on* prop (a lowercase on* name must resolve to a real DOM-event handler). @lostgradient/cinder's SourceDiffViewer already established the precedent (onFilesChange, onAnnotationSelectionChange) of keeping the field name and payload exactly as specified while translating only the casing to this repository's house style.

diff-review, review-editor.

Live preview

No comments yet.

02

When to use

Use when
  • Building a standalone comment panel next to a `DiffViewer` or `SourceDiffViewer` instance.
  • Composing the aggregate `DiffReview` shell, which renders this panel internally.
Avoid when
  • Reviewing ProseMirror document threads with author identity — use ReviewEditor's own comment UI instead.
03

Examples

Read-only review

In read-only mode the filter and review note remain viewable but every mutating control is disabled.

Standalone comments panel

DiffReviewComments next to a DiffViewer, sharing one controlled DiffReviewState.

Reviewing only open feedback

Toggling to the unresolved-only filter hides resolved comments without discarding them.

04

Props

Props for diff-review-comments
Name
Type
Default
Description
state required DiffReviewState Controlled diff-review session state. Never mutated directly.
onStateChange required (next: DiffReviewState) => void Called whenever a comment/review-note action succeeds. Named onStateChange (camelCase) rather than the cross-package contract's lowercase onstatechange: this repository's check-prop-conventions gate structurally bans a non-native-passthrough lowercase on* prop (see @lostgradient/cinder's SourceDiffViewer.onFilesChange for the same, earlier translation) — the field name and payload are otherwise exactly the contract's.
readonly boolean false Disables create/edit/delete/resolve/reopen and review-note editing. Viewing and filtering remain available.
defaultFilter 'all' | 'unresolved' 'all' Initial comment filter. Uncontrolled after mount — the person can change it locally.
onNavigate (comment: DiffReviewComment) => void Called when a person activates a CURRENT comment's "Go to" action -- never for an outdated or removed one, which instead moves focus to that comment's own captured-detail text in place. The host uses this to focus the comment's anchor in whichever diff viewer it owns (through that viewer's own public focusAnchor/focusFile, e.g. DiffReview's internal DiffReviewRenderer, or a standalone viewer's own ref in a host that composes this panel next to one directly).