# `MingaEditor.Agent.DiffReview`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_editor/agent/diff_review.ex#L1)

Pure data structure for reviewing agent file edits.

Wraps the hunks produced by `Git.Diff.diff_lines/2` with navigation
state and per-hunk resolution tracking. The file viewer renders in
"diff mode" when a `DiffReview` is present, and the user can accept
or reject individual hunks without leaving the agentic view.

# `diff_line`

```elixir
@type diff_line() ::
  {String.t(), :context | :added | :removed | :hunk_header,
   non_neg_integer() | nil}
```

# `resolution`

```elixir
@type resolution() :: :accepted | :rejected
```

Resolution status for a single hunk.

# `t`

```elixir
@type t() :: %MingaEditor.Agent.DiffReview{
  after_lines: [String.t()],
  before_lines: [String.t()],
  current_hunk_index: non_neg_integer(),
  hunks: [Minga.Core.Diff.hunk()],
  path: String.t(),
  resolutions: %{required(non_neg_integer()) =&gt; resolution()}
}
```

Diff review state.

# `accept_all`

```elixir
@spec accept_all(t()) :: t()
```

Accepts all unresolved hunks.

# `accept_current`

```elixir
@spec accept_current(t()) :: t()
```

Marks the current hunk as accepted and advances to the next unresolved.

# `current_hunk`

```elixir
@spec current_hunk(t()) :: Minga.Core.Diff.hunk() | nil
```

Returns the current hunk, or nil if no hunks exist.

# `current_hunk_line`

```elixir
@spec current_hunk_line(t()) :: non_neg_integer()
```

Returns the start line of the current hunk in the after-content,
suitable for scrolling the viewer to show it.

# `new`

```elixir
@spec new(String.t(), String.t(), String.t()) :: t() | nil
```

Creates a new DiffReview from a file path and before/after content strings.

Splits content into lines, computes hunks via `Git.Diff.diff_lines/2`,
and initializes with no resolutions and the cursor on the first hunk.
Returns `nil` if there are no hunks (no changes detected).

# `next_hunk`

```elixir
@spec next_hunk(t()) :: t()
```

Moves to the next unresolved hunk. Wraps around to the first.

# `prev_hunk`

```elixir
@spec prev_hunk(t()) :: t()
```

Moves to the previous unresolved hunk. Wraps around to the last.

# `reject_all`

```elixir
@spec reject_all(t()) :: t()
```

Rejects all unresolved hunks.

# `reject_current`

```elixir
@spec reject_current(t()) :: t()
```

Marks the current hunk as rejected and advances to the next unresolved.

# `resolution_at`

```elixir
@spec resolution_at(t(), non_neg_integer()) :: resolution() | nil
```

Returns the resolution for a given hunk index, or nil if unresolved.

# `resolved?`

```elixir
@spec resolved?(t()) :: boolean()
```

Returns true when every hunk has been accepted or rejected.

# `summary`

```elixir
@spec summary(t()) :: {non_neg_integer(), non_neg_integer()}
```

Returns `{added_count, removed_count}` summarizing the diff.

Counts lines added and removed across all hunks.

# `to_display_lines`

```elixir
@spec to_display_lines(t()) :: [diff_line()]
```

Builds the list of lines to display in unified diff format.

Returns a list of `{text, type, hunk_index_or_nil}` tuples where
`type` is `:context`, `:added`, `:removed`, or `:hunk_header`.
`hunk_index_or_nil` links display lines back to their hunk for
resolution marker rendering.

# `update_after`

```elixir
@spec update_after(t(), String.t()) :: t() | nil
```

Updates the diff review with new after-content while preserving
resolutions for hunks that have not changed.

Used for accumulated diffs: when the agent edits the same file again,
the baseline stays the same but the after-content changes. Hunks are
re-computed and resolutions from the previous review are carried forward
for hunks that still match.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
