import { useState } from "react"; import { cn } from "@/lib/utils"; import { hunkLabel } from "./diff-lines"; import type { TaskChatDiffLine } from "./task-chat-model"; /** `+12 −3` in the diff hues. Unknown counts are omitted, not shown as zero. */ export function TaskChatDiffCounts({ added, removed, className, }: { added: number | null | undefined; removed: number | null | undefined; className?: string; }) { if (added == null && removed == null) return null; // A pure addition or deletion reads cleaner without the empty side. const showAdded = added != null && (added > 0 || !removed); const showRemoved = removed != null && (removed > 0 || !added); return ( {showAdded ? +{added} : null} {showRemoved ? −{removed} : null} ); } const MARKER: Record = { add: "+", remove: "−", context: " ", hunk: "", file: "", }; /** * Compact unified diff for one file. Long diffs show the first `previewLines` * lines with a control to reveal the rest; the block scrolls inside a fixed * maximum height so a large edit never pushes the conversation away. */ export function TaskChatDiffView({ lines, truncated = false, previewLines = 24, className, }: { lines: readonly TaskChatDiffLine[]; truncated?: boolean; previewLines?: number; className?: string; }) { const [showAll, setShowAll] = useState(false); const visible = showAll ? lines : lines.slice(0, previewLines); const hidden = lines.length - visible.length; return (
{visible.map((line, index) => line.kind === "file" ? ( ) : line.kind === "hunk" ? ( ) : ( ), )}
{line.text}
{hunkLabel(line.text)}
{MARKER[line.kind]} {line.kind === "add" ? "Added: " : line.kind === "remove" ? "Removed: " : ""} {line.text || " "}
{hidden > 0 || truncated ? (
{hidden > 0 ? ( ) : null} {truncated && hidden === 0 ? Diff shortened for display. : null}
) : null}
); }