feat: Add Diff View and File History Tracking

- Implemented a new Diff View in the Workbench to visualize file changes
- Added file history tracking with detailed change information
- Enhanced FileTree and FileModifiedDropdown to show line additions and deletions
- Integrated file history saving and retrieval in ActionRunner
- Updated Workbench view types to include 'diff' option
- Added support for inline and side-by-side diff view modes
This commit is contained in:
Toddyclipsgg
2025-02-23 07:55:38 -03:00
parent 8c72ed76b3
commit ab6f5328b4
17 changed files with 1192 additions and 77 deletions

View File

@@ -3,6 +3,8 @@ import type { FileMap } from '~/lib/stores/files';
import { classNames } from '~/utils/classNames';
import { createScopedLogger, renderLogger } from '~/utils/logger';
import * as ContextMenu from '@radix-ui/react-context-menu';
import type { FileHistory } from '~/types/actions';
import { diffLines, type Change } from 'diff';
const logger = createScopedLogger('FileTree');
@@ -19,6 +21,7 @@ interface Props {
allowFolderSelection?: boolean;
hiddenFiles?: Array<string | RegExp>;
unsavedFiles?: Set<string>;
fileHistory?: Record<string, FileHistory>;
className?: string;
}
@@ -34,6 +37,7 @@ export const FileTree = memo(
hiddenFiles,
className,
unsavedFiles,
fileHistory = {},
}: Props) => {
renderLogger.trace('FileTree');
@@ -138,6 +142,7 @@ export const FileTree = memo(
selected={selectedFile === fileOrFolder.fullPath}
file={fileOrFolder}
unsavedChanges={unsavedFiles?.has(fileOrFolder.fullPath)}
fileHistory={fileHistory}
onCopyPath={() => {
onCopyPath(fileOrFolder);
}}
@@ -253,19 +258,55 @@ interface FileProps {
file: FileNode;
selected: boolean;
unsavedChanges?: boolean;
fileHistory?: Record<string, FileHistory>;
onCopyPath: () => void;
onCopyRelativePath: () => void;
onClick: () => void;
}
function File({
file: { depth, name },
file: { depth, name, fullPath },
onClick,
onCopyPath,
onCopyRelativePath,
selected,
unsavedChanges = false,
fileHistory = {},
}: FileProps) {
const fileModifications = fileHistory[fullPath];
const hasModifications = fileModifications !== undefined;
// Calculate added and removed lines from the most recent changes
const { additions, deletions } = useMemo(() => {
if (!fileModifications?.originalContent) return { additions: 0, deletions: 0 };
// Usar a mesma lógica do DiffView para processar as mudanças
const normalizedOriginal = fileModifications.originalContent.replace(/\r\n/g, '\n');
const normalizedCurrent = fileModifications.versions[fileModifications.versions.length - 1]?.content.replace(/\r\n/g, '\n') || '';
if (normalizedOriginal === normalizedCurrent) {
return { additions: 0, deletions: 0 };
}
const changes = diffLines(normalizedOriginal, normalizedCurrent, {
newlineIsToken: false,
ignoreWhitespace: true,
ignoreCase: false
});
return changes.reduce((acc: { additions: number; deletions: number }, change: Change) => {
if (change.added) {
acc.additions += change.value.split('\n').length;
}
if (change.removed) {
acc.deletions += change.value.split('\n').length;
}
return acc;
}, { additions: 0, deletions: 0 });
}, [fileModifications]);
const showStats = additions > 0 || deletions > 0;
return (
<FileContextMenu onCopyPath={onCopyPath} onCopyRelativePath={onCopyRelativePath}>
<NodeButton
@@ -286,7 +327,21 @@ function File({
})}
>
<div className="flex-1 truncate pr-2">{name}</div>
{unsavedChanges && <span className="i-ph:circle-fill scale-68 shrink-0 text-orange-500" />}
<div className="flex items-center gap-1">
{showStats && (
<div className="flex items-center gap-1 text-xs">
{additions > 0 && (
<span className="text-green-500">+{additions}</span>
)}
{deletions > 0 && (
<span className="text-red-500">-{deletions}</span>
)}
</div>
)}
{unsavedChanges && (
<span className="i-ph:circle-fill scale-68 shrink-0 text-orange-500" />
)}
</div>
</div>
</NodeButton>
</FileContextMenu>