Files
2026-01-24 16:52:19 -06:00

104 lines
2.9 KiB
TypeScript

'use client';
import { useMemo } from 'react';
import { diffLines } from 'diff';
interface DiffPreviewProps {
oldContent: string;
newContent: string;
}
interface DiffLine {
type: 'added' | 'removed' | 'unchanged';
content: string;
oldLineNum?: number;
newLineNum?: number;
}
export function DiffPreview({ oldContent, newContent }: DiffPreviewProps) {
const diffResult = useMemo(() => {
const changes = diffLines(oldContent, newContent);
const lines: DiffLine[] = [];
let oldLineNum = 1;
let newLineNum = 1;
changes.forEach((change) => {
const changeLines = change.value.split('\n');
// Remove the last empty line if the value ends with \n
if (changeLines[changeLines.length - 1] === '') {
changeLines.pop();
}
changeLines.forEach((line) => {
if (change.added) {
lines.push({
type: 'added',
content: line,
newLineNum: newLineNum++,
});
} else if (change.removed) {
lines.push({
type: 'removed',
content: line,
oldLineNum: oldLineNum++,
});
} else {
lines.push({
type: 'unchanged',
content: line,
oldLineNum: oldLineNum++,
newLineNum: newLineNum++,
});
}
});
});
return lines;
}, [oldContent, newContent]);
const stats = useMemo(() => {
let added = 0;
let removed = 0;
diffResult.forEach((line) => {
if (line.type === 'added') added++;
if (line.type === 'removed') removed++;
});
return { added, removed };
}, [diffResult]);
return (
<div className="diff-preview-container">
<div className="diff-preview-header">
<span className="diff-preview-title">Changes Preview</span>
<div className="diff-preview-stats">
<span className="diff-stat-added">+{stats.added}</span>
<span className="diff-stat-removed">-{stats.removed}</span>
</div>
</div>
<div className="diff-preview-content">
{diffResult.map((line, index) => (
<div
key={index}
className={`diff-preview-line diff-preview-${line.type}`}
>
<span className="diff-preview-gutter">
{line.type === 'removed' && <span className="diff-sign">-</span>}
{line.type === 'added' && <span className="diff-sign">+</span>}
{line.type === 'unchanged' && <span className="diff-sign"> </span>}
</span>
<span className="diff-preview-line-num diff-preview-old-num">
{line.oldLineNum || ''}
</span>
<span className="diff-preview-line-num diff-preview-new-num">
{line.newLineNum || ''}
</span>
<span className="diff-preview-code">
{line.content || '\u00A0'}
</span>
</div>
))}
</div>
</div>
);
}