import { useMemo, useState } from "react"; interface DiffHunk { oldStart: number; newStart: number; lines: DiffLine[]; } interface DiffLine { type: "add" | "del" | "ctx"; content: string; oldLine?: number; newLine?: number; } interface DiffFile { path: string; hunks: DiffHunk[]; } interface DiffViewProps { files: DiffFile[]; } function parseUnifiedDiff(diff: string): DiffFile[] { const files: DiffFile[] = []; let currentFile: DiffFile | null = null; let currentHunk: DiffHunk | null = null; for (const line of diff.split("\n")) { if (line.startsWith("--- ") || line.startsWith("+++ ")) continue; if (line.startsWith("diff --git ")) { if (currentFile && currentFile.hunks.length > 0) files.push(currentFile); const match = line.match(/diff --git a\/(.+) b\/(.+)/); currentFile = { path: match?.[2] ?? match?.[1] ?? "unknown", hunks: [], }; currentHunk = null; continue; } if (line.startsWith("@@ ")) { const match = line.match(/@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/); if (currentFile) { currentHunk = { oldStart: match ? parseInt(match[1]) : 0, newStart: match ? parseInt(match[2]) : 0, lines: [], }; currentFile.hunks.push(currentHunk); } continue; } if (!currentHunk) continue; if (line.startsWith("+")) { currentHunk.lines.push({ type: "add", content: line.slice(1), newLine: currentHunk.newStart + currentHunk.lines.filter((l) => l.type !== "del").length, }); } else if (line.startsWith("-")) { currentHunk.lines.push({ type: "del", content: line.slice(1), oldLine: currentHunk.oldStart + currentHunk.lines.filter((l) => l.type !== "add").length, }); } else { const ctxLine = line.startsWith(" ") ? line.slice(1) : line; currentHunk.lines.push({ type: "ctx", content: ctxLine }); } } if (currentFile && currentFile.hunks.length > 0) files.push(currentFile); return files; } export function DiffView({ files: rawFiles }: DiffViewProps) { return (
| {line.type === "del" ? (line.oldLine ?? "") : ""} | {line.type === "add" ? (line.newLine ?? "") : line.type === "ctx" ? (line.oldLine ?? "") : ""} | {line.type === "add" ? "+" : line.type === "del" ? "-" : " "} | {line.content} |