Fix workspace diff multi-patch rendering

This commit is contained in:
Dotta committed 2026-05-15 12:54:27 -05:00
1 parent d15fb5de01
commit af5840bc41
3 files changed
+144 -18

No files matched your search

@@ -7,6 +7,17 @@ import type {
export type DiffRenderMode = "unified" | "split";
export interface DiffPatchViewModel {
kind: WorkspaceDiffFilePatch["kind"];
patch: string | null;
additions: number;
deletions: number;
binary: boolean;
oversized: boolean;
truncated: boolean;
warnings: WorkspaceDiffWarning[];
}
export interface DiffFileViewModel {
path: string;
oldPath: string | null;
@@ -18,6 +29,7 @@ export interface DiffFileViewModel {
truncated: boolean;
warnings: WorkspaceDiffWarning[];
patchKinds: WorkspaceDiffFilePatch["kind"][];
patches: DiffPatchViewModel[];
patch: string | null;
}
@@ -47,17 +59,30 @@ export function fileName(filePath: string) {
return filePath.split("/").filter(Boolean).pop() ?? filePath;
}
export function buildFilePatches(file: WorkspaceDiffFile): DiffPatchViewModel[] {
return file.patches.map((patch) => {
const textPatch = patch.patch?.trimEnd() ?? null;
return {
kind: patch.kind,
patch: textPatch && textPatch.length > 0 ? textPatch : null,
additions: patch.additions,
deletions: patch.deletions,
binary: patch.binary,
oversized: patch.oversized,
truncated: patch.truncated,
warnings: patch.warnings,
};
});
}
export function buildFilePatch(file: WorkspaceDiffFile): string | null {
const patches = file.patches
.map((patch) => patch.patch?.trimEnd() ?? "")
.filter(Boolean);
if (patches.length === 0) return null;
return patches.join("\n");
return buildFilePatches(file).find((patch) => patch.patch)?.patch ?? null;
}
export function toFileViewModels(diff: WorkspaceDiffResponse | null | undefined): DiffFileViewModel[] {
return (diff?.files ?? []).map((file) => {
const patchWarnings = file.patches.flatMap((patch) => patch.warnings);
const patches = buildFilePatches(file);
return {
path: file.path,
oldPath: file.oldPath,
@@ -69,7 +94,8 @@ export function toFileViewModels(diff: WorkspaceDiffResponse | null | undefined)
truncated: file.truncated,
warnings: [...file.warnings, ...patchWarnings],
patchKinds: file.patches.map((patch) => patch.kind),
patch: buildFilePatch(file),
patches,
patch: patches.find((patch) => patch.patch)?.patch ?? null,
};
});
}
@@ -10,6 +10,7 @@ import {
statusLabel,
toFileViewModels,
type DiffFileViewModel,
type DiffPatchViewModel,
type DiffRenderMode,
} from "../diff-model.js";
@@ -38,7 +39,27 @@ function warningText(file: DiffFileViewModel) {
if (file.oversized) return "Too large to render";
if (file.truncated) return "Patch truncated";
if (file.warnings.length > 0) return file.warnings[0]?.message ?? "Diff warning";
if (!file.patch) return "No text patch";
if (file.patches.every((patch) => !patch.patch)) return "No text patch";
return null;
}
const PATCH_KIND_LABELS: Record<DiffPatchViewModel["kind"], string> = {
staged: "Staged",
unstaged: "Unstaged",
head: "Head",
untracked: "Untracked",
};
function patchKindLabel(kind: DiffPatchViewModel["kind"]) {
return PATCH_KIND_LABELS[kind] ?? "Patch";
}
function patchWarningText(patch: DiffPatchViewModel) {
if (patch.binary) return "Binary file";
if (patch.oversized) return "Too large to render";
if (patch.truncated) return "Patch truncated";
if (patch.warnings.length > 0) return patch.warnings[0]?.message ?? "Diff warning";
if (!patch.patch) return "No text patch";
return null;
}
@@ -139,7 +160,7 @@ function FileDiffPanel({
mode: DiffRenderMode;
}) {
const warning = warningText(file);
if (warning || !file.patch) {
if (warning) {
return (
<div className="border border-dashed border-border bg-background px-4 py-6 text-sm text-muted-foreground">
{warning ?? "No renderable patch is available for this file."}
@@ -148,16 +169,36 @@ function FileDiffPanel({
}
return (
<div className="overflow-hidden border border-border bg-background">
<PatchDiff
patch={file.patch}
options={{
diffStyle: mode,
overflow: "scroll",
disableLineNumbers: false,
themeType: "system",
}}
/>
<div className="space-y-3">
{file.patches.map((patch) => {
const patchWarning = patchWarningText(patch);
return (
<div key={patch.kind} className="overflow-hidden border border-border bg-background">
{file.patches.length > 1 ? (
<div className="flex items-center gap-2 border-b border-border bg-muted/30 px-3 py-2 text-xs text-muted-foreground">
<span className="font-medium text-foreground">{patchKindLabel(patch.kind)}</span>
<span className="font-mono text-emerald-700 dark:text-emerald-300">+{patch.additions}</span>
<span className="font-mono text-red-700 dark:text-red-300">-{patch.deletions}</span>
</div>
) : null}
{patchWarning || !patch.patch ? (
<div className="px-4 py-6 text-sm text-muted-foreground">
{patchWarning ?? "No renderable patch is available for this file."}
</div>
) : (
<PatchDiff
patch={patch.patch}
options={{
diffStyle: mode,
overflow: "scroll",
disableLineNumbers: false,
themeType: "system",
}}
/>
)}
</div>
);
})}
</div>
);
}