import type { RepositoryRow } from "../db/index.ts"; import type { RenderedDiffFile } from "../services/diffHighlight.ts"; export function slugify(s: string): string { return `diff-${s.replace(/[^a-zA-Z0-9]/g, "-")}`; } export function statusLetter(status: string): string { return ( ( { added: "A", deleted: "D", modified: "M", renamed: "R", copied: "C", } as Record )[status] ?? "M" ); } export type FileTreeNode = | { type: "file"; file: RenderedDiffFile } | { type: "dir"; children: Map }; export function buildFileTree( files: RenderedDiffFile[], ): Map { const root = new Map(); for (const f of files) { const p = f.newPath || f.oldPath; const parts = p.split("/"); let current = root; for (let i = 0; i < parts.length - 1; i++) { const part = parts[i]!; if (!current.has(part)) { current.set(part, { type: "dir", children: new Map() }); } const node = current.get(part)!; if (node.type === "dir") current = node.children; } current.set(parts[parts.length - 1]!, { type: "file", file: f }); } return root; } export function renderFileTree(tree: Map): JSX.Element { return ( ); } interface DiffViewProps { files: RenderedDiffFile[]; repo: RepositoryRow; /** If provided, an extra "view at sha" link is shown in each file header. */ sha?: string; } export function DiffView({ files, repo, sha }: DiffViewProps) { const totalAdded = files.reduce((s, f) => s + f.added, 0); const totalRemoved = files.reduce((s, f) => s + f.removed, 0); const changedStr = [ `${files.length} file${files.length !== 1 ? "s" : ""} changed`, totalAdded > 0 ? `${totalAdded} insertion${totalAdded !== 1 ? "s" : ""}(+)` : "", totalRemoved > 0 ? `${totalRemoved} deletion${totalRemoved !== 1 ? "s" : ""}(-)` : "", ] .filter(Boolean) .join(", "); return ( <> {files.length > 0 && (
{changedStr}
)} {files.length === 0 ? (

Empty diff or no diff available.

) : (
{files.map((f) => { const displayPath = f.newPath || f.oldPath; const sl = statusLetter(f.status); return (
▾ {sl} {displayPath} {f.status === "renamed" && f.oldPath !== f.newPath && ( ← {f.oldPath} )}
{f.isBinary ? ( {f.binarySize ? ( <> {"Bin "} { f.binarySize .before } {" \u2192 "} { f.binarySize .after } {" bytes"} ) : ( "Binary" )} ) : ( <> {f.added > 0 && ( +{f.added} )} {f.removed > 0 && ( -{f.removed} )} )} {f.status !== "deleted" && ( <> {!!sha && ( @ {sha.slice(0, 7)} )} @ {repo.default_branch} )}
{f.isBinary ? (
Binary file — not shown
) : f.hunks.length === 0 ? (
No textual changes.
) : (
{f.hunks.map((hunk) => (
{hunk.rows.map( (row) => ( ), )}
{ hunk.header }
{row.oldLine ?? ""} {row.newLine ?? ""} {row.type === "add" ? "+" : row.type === "del" ? "-" : " "} { row.html as "safe" }
))}
)}
); })}
)} ); }