@using System.Text.Json @* LineDiffView (Component #24, Task M8 E2). Renders a single Code field's "lineDiff" payload — the line-level diff that the importer attaches to a Modified ImportPreviewItem's FieldDiffJson for code fields — as a compact, GitHub-style +/- list. Pure presentation: it takes an already-parsed JsonElement (the value of the "lineDiff" key) and walks its "hunks" array. hunk op ∈ "context" | "add" | "remove" (lowercase): context → muted line, both old/new line numbers add → green-ish line, new line number only (no oldLineNo) remove → red-ish line, old line number only (no newLineNo) When the payload's "truncated" flag is true a trailing marker summarises the addedCount / removedCount the diff could not show in full. No third-party diff/charting library — Bootstrap utility classes + a small monospace block only. *@ @if (_hunks.Count == 0 && !_truncated) {
No line-level changes.
} else {
@foreach (var hunk in _hunks) { var (rowCls, gutter, sign) = hunk.Op switch { "add" => ("bg-success-subtle text-success-emphasis", FormatGutter(null, hunk.NewLineNo), "+"), "remove" => ("bg-danger-subtle text-danger-emphasis", FormatGutter(hunk.OldLineNo, null), "-"), _ => ("text-body-secondary", FormatGutter(hunk.OldLineNo, hunk.NewLineNo), " "), };
@gutter @sign @hunk.Text
}
@if (_truncated) {
… diff truncated (+@_addedCount / -@_removedCount more)
} } @code { /// /// The parsed value of a Modified item's FieldDiffJson code field's /// lineDiff key. When null the component renders nothing meaningful — /// callers should only render it for code fields that actually carry a /// lineDiff object. /// [Parameter] public JsonElement? LineDiff { get; set; } private readonly List _hunks = new(); private bool _truncated; private int _addedCount; private int _removedCount; private sealed record Hunk(string Op, string Text, int? OldLineNo, int? NewLineNo); protected override void OnParametersSet() { _hunks.Clear(); _truncated = false; _addedCount = 0; _removedCount = 0; if (LineDiff is not { ValueKind: JsonValueKind.Object } payload) { return; } if (payload.TryGetProperty("truncated", out var truncatedEl) && truncatedEl.ValueKind is JsonValueKind.True or JsonValueKind.False) { _truncated = truncatedEl.GetBoolean(); } if (payload.TryGetProperty("addedCount", out var addedEl) && addedEl.ValueKind == JsonValueKind.Number) { _addedCount = addedEl.GetInt32(); } if (payload.TryGetProperty("removedCount", out var removedEl) && removedEl.ValueKind == JsonValueKind.Number) { _removedCount = removedEl.GetInt32(); } if (payload.TryGetProperty("hunks", out var hunksEl) && hunksEl.ValueKind == JsonValueKind.Array) { foreach (var h in hunksEl.EnumerateArray()) { if (h.ValueKind != JsonValueKind.Object) { continue; } var op = h.TryGetProperty("op", out var opEl) && opEl.ValueKind == JsonValueKind.String ? opEl.GetString() ?? "context" : "context"; var text = h.TryGetProperty("text", out var textEl) && textEl.ValueKind == JsonValueKind.String ? textEl.GetString() ?? string.Empty : string.Empty; int? oldLineNo = h.TryGetProperty("oldLineNo", out var oldEl) && oldEl.ValueKind == JsonValueKind.Number ? oldEl.GetInt32() : null; int? newLineNo = h.TryGetProperty("newLineNo", out var newEl) && newEl.ValueKind == JsonValueKind.Number ? newEl.GetInt32() : null; _hunks.Add(new Hunk(op, text, oldLineNo, newLineNo)); } } } private static string FormatGutter(int? oldLineNo, int? newLineNo) { var left = oldLineNo?.ToString() ?? string.Empty; var right = newLineNo?.ToString() ?? string.Empty; return $"{left,3} {right,3}"; } }