import { GitBranch, GitCommit, RefreshCw } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import type { ConfirmationRequest, FileStatusCode, GitDiffMap, GitStatusResponse } from '../../types/types'; import { getAllChangedFiles, hasChangedFiles } from '../../utils/gitPanelUtils'; import CommitComposer from './CommitComposer'; import FileChangeList from './FileChangeList'; import FileSelectionControls from './FileSelectionControls'; import FileStatusLegend from './FileStatusLegend'; type ChangesViewProps = { isMobile: boolean; projectPath: string; gitStatus: GitStatusResponse | null; gitDiff: GitDiffMap; isLoading: boolean; wrapText: boolean; isCreatingInitialCommit: boolean; onWrapTextChange: (wrapText: boolean) => void; onCreateInitialCommit: () => Promise; onOpenFile: (filePath: string) => Promise; onDiscardFile: (filePath: string) => Promise; onDeleteFile: (filePath: string) => Promise; onCommitChanges: (message: string, files: string[]) => Promise; onGenerateCommitMessage: (files: string[]) => Promise; onRequestConfirmation: (request: ConfirmationRequest) => void; onExpandedFilesChange: (hasExpandedFiles: boolean) => void; }; export default function ChangesView({ isMobile, projectPath, gitStatus, gitDiff, isLoading, wrapText, isCreatingInitialCommit, onWrapTextChange, onCreateInitialCommit, onOpenFile, onDiscardFile, onDeleteFile, onCommitChanges, onGenerateCommitMessage, onRequestConfirmation, onExpandedFilesChange, }: ChangesViewProps) { const [expandedFiles, setExpandedFiles] = useState>(new Set()); const [selectedFiles, setSelectedFiles] = useState>(new Set()); const changedFiles = useMemo(() => getAllChangedFiles(gitStatus), [gitStatus]); const hasExpandedFiles = expandedFiles.size > 0; useEffect(() => { if (!gitStatus || gitStatus.error) { setSelectedFiles(new Set()); return; } // Preserve previous behavior: every fresh status snapshot reselects changed files. setSelectedFiles(new Set(getAllChangedFiles(gitStatus))); }, [gitStatus]); useEffect(() => { onExpandedFilesChange(hasExpandedFiles); }, [hasExpandedFiles, onExpandedFilesChange]); useEffect(() => { return () => { onExpandedFilesChange(false); }; }, [onExpandedFilesChange]); const toggleFileExpanded = useCallback((filePath: string) => { setExpandedFiles((previous) => { const next = new Set(previous); if (next.has(filePath)) { next.delete(filePath); } else { next.add(filePath); } return next; }); }, []); const toggleFileSelected = useCallback((filePath: string) => { setSelectedFiles((previous) => { const next = new Set(previous); if (next.has(filePath)) { next.delete(filePath); } else { next.add(filePath); } return next; }); }, []); const requestFileAction = useCallback( (filePath: string, status: FileStatusCode) => { if (status === 'U') { onRequestConfirmation({ type: 'delete', message: `Delete untracked file "${filePath}"? This action cannot be undone.`, onConfirm: async () => { await onDeleteFile(filePath); }, }); return; } onRequestConfirmation({ type: 'discard', message: `Discard all changes to "${filePath}"? This action cannot be undone.`, onConfirm: async () => { await onDiscardFile(filePath); }, }); }, [onDeleteFile, onDiscardFile, onRequestConfirmation], ); const commitSelectedFiles = useCallback( (message: string) => { return onCommitChanges(message, Array.from(selectedFiles)); }, [onCommitChanges, selectedFiles], ); const generateMessageForSelection = useCallback(() => { return onGenerateCommitMessage(Array.from(selectedFiles)); }, [onGenerateCommitMessage, selectedFiles]); return ( <> {gitStatus && !gitStatus.error && ( setSelectedFiles(new Set(changedFiles))} onDeselectAll={() => setSelectedFiles(new Set())} /> )} {!gitStatus?.error && }
{isLoading ? (
) : gitStatus?.hasCommits === false ? (

No commits yet

This repository doesn't have any commits yet. Create your first commit to start tracking changes.

) : !gitStatus || !hasChangedFiles(gitStatus) ? (

No changes detected

) : (
{ void onOpenFile(filePath); }} onToggleWrapText={() => onWrapTextChange(!wrapText)} onRequestFileAction={requestFileAction} />
)}
); }