104 lines
2.9 KiB
TypeScript
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>
|
|
);
|
|
}
|