'use client';
import { useMemo, useState } from 'react';
import { ChevronDown, ChevronRight } from 'lucide-react';
import { Button } from '@spoon/ui';
import type { DiffMode } from './diff-file-view';
import type { DiffFileStatus, ParsedDiffFile } from './diff-utils';
import { DiffFileView, useDiffTheme } from './diff-file-view';
import { parseDiffFiles } from './diff-utils';
const statusBadge: Record<
DiffFileStatus,
{ label: string; className: string }
> = {
added: { label: 'Added', className: 'bg-emerald-500/15 text-emerald-500' },
deleted: { label: 'Deleted', className: 'bg-red-500/15 text-red-500' },
modified: { label: 'Modified', className: 'bg-amber-500/15 text-amber-500' },
renamed: { label: 'Renamed', className: 'bg-sky-500/15 text-sky-500' },
};
const totals = (files: ParsedDiffFile[]) =>
files.reduce(
(acc, file) => ({
additions: acc.additions + file.additions,
deletions: acc.deletions + file.deletions,
}),
{ additions: 0, deletions: 0 },
);
const FileCard = ({
file,
mode,
theme,
defaultOpen,
}: {
file: ParsedDiffFile;
mode: DiffMode;
theme: 'light' | 'dark';
defaultOpen: boolean;
}) => {
const [open, setOpen] = useState(defaultOpen);
const badge = statusBadge[file.status];
return (
{open ? (
) : null}
);
};
export const DiffViewer = ({
diff,
focusedPath,
onRefresh,
onClearFocusedPath,
}: {
diff: string;
focusedPath?: string;
onRefresh: () => Promise;
onClearFocusedPath?: () => void;
}) => {
const [mode, setMode] = useState('unified');
const theme = useDiffTheme();
const files = useMemo(() => parseDiffFiles(diff), [diff]);
const normalizedFocus = focusedPath?.replace(/^\.\/+/, '');
const visibleFiles = useMemo(
() =>
normalizedFocus
? files.filter(
(file) =>
file.displayPath === normalizedFocus ||
file.newPath === normalizedFocus ||
file.oldPath === normalizedFocus,
)
: files,
[files, normalizedFocus],
);
const stats = totals(visibleFiles);
return (
{focusedPath ? `Diff: ${focusedPath}` : 'Diff viewer'}
{visibleFiles.length > 0
? `${visibleFiles.length} ${visibleFiles.length === 1 ? 'file' : 'files'}, `
: ''}
+{stats.additions}{' '}
−{stats.deletions}
{focusedPath ? (
) : null}
{visibleFiles.length > 0 ? (
{visibleFiles.map((file, index) => (
))}
) : (
{focusedPath
? 'No diff is recorded for this file yet.'
: 'No workspace diff yet.'}
)}
);
};