import React from 'react'; import { FileIcon } from '@renderer/components/team/editor/FileIcon'; import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip'; import { shortcutLabel } from '@renderer/utils/platformKeys'; import { ChevronDown, ChevronRight, FilePlus, Loader2, Save, Undo2 } from 'lucide-react'; import type { FileChangeWithContent, HunkDecision } from '@shared/types'; import type { FileChangeSummary } from '@shared/types/review'; const CONTENT_SOURCE_LABELS: Record = { 'file-history': 'File History', 'snippet-reconstruction': 'Reconstructed', 'disk-current': 'Current Disk', 'git-fallback': 'Git Fallback', unavailable: 'Missing on disk', }; interface FileSectionHeaderProps { file: FileChangeSummary; fileContent: FileChangeWithContent | null; fileDecision: HunkDecision | undefined; externalChange?: { type: 'change' | 'add' | 'unlink' }; pathChangeLabel?: | { kind: 'deleted' } | { kind: 'moved' | 'renamed'; direction: 'from' | 'to'; otherPath: string }; hasEdits: boolean; applying: boolean; isCollapsed: boolean; onToggleCollapse: (filePath: string) => void; onDiscard: (filePath: string) => void; onSave: (filePath: string) => void; onReloadFromDisk?: (filePath: string) => void; onKeepDraft?: (filePath: string) => void; onRestoreMissingFile?: (filePath: string, content: string) => void; onAcceptFile?: (filePath: string) => void; onRejectFile?: (filePath: string) => void; } export const FileSectionHeader = ({ file, fileContent, fileDecision, externalChange, pathChangeLabel, hasEdits, applying, isCollapsed, onToggleCollapse, onDiscard, onSave, onReloadFromDisk, onKeepDraft, onRestoreMissingFile, onAcceptFile, onRejectFile, }: FileSectionHeaderProps): React.ReactElement => { const isMissingOnDisk = fileContent ? fileContent.modifiedFullContent == null : false; const isPreviewOnly = isMissingOnDisk || fileContent?.contentSource === 'unavailable'; const restoreContent = fileContent?.modifiedFullContent ?? (() => { const writeSnippets = file.snippets.filter( (s) => !s.isError && (s.type === 'write-new' || s.type === 'write-update') ); if (writeSnippets.length === 0) return null; return writeSnippets[writeSnippets.length - 1].newString; })(); const canRestore = !!onRestoreMissingFile && isPreviewOnly && !hasEdits && restoreContent != null; const externalChangeLabel = externalChange?.type === 'unlink' ? 'Deleted on disk' : externalChange?.type === 'add' ? 'Recreated on disk' : externalChange?.type === 'change' ? 'Changed on disk' : null; const handleHeaderClick = (e: React.MouseEvent): void => { // Don't collapse when clicking action buttons if ((e.target as HTMLElement).closest('[data-no-collapse]')) return; onToggleCollapse(file.filePath); }; const handleHeaderKeyDown = (e: React.KeyboardEvent): void => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onToggleCollapse(file.filePath); } }; return (
{isCollapsed ? : } {file.relativePath} {file.isNewFile && ( NEW )} {pathChangeLabel?.kind === 'deleted' && ( DELETED )} {pathChangeLabel && pathChangeLabel.kind !== 'deleted' && ( {pathChangeLabel.kind.toUpperCase()} {pathChangeLabel.direction === 'from' ? 'From' : 'To'} {pathChangeLabel.otherPath} )} {fileContent?.contentSource && ( {isPreviewOnly ? 'Missing on disk' : (CONTENT_SOURCE_LABELS[fileContent.contentSource] ?? fileContent.contentSource)} {isPreviewOnly ? (
File is missing on disk
We can still show a preview from agent logs, but your filesystem is out of sync.
{restoreContent != null ? (
Use Restore to write the preview content back to disk.
) : (
Full file content is not available to restore automatically.
)}
) : ( {CONTENT_SOURCE_LABELS[fileContent.contentSource] ?? fileContent.contentSource} )}
)} {fileDecision && ( {fileDecision} )} {externalChangeLabel && ( {externalChangeLabel} )}
{externalChange && onReloadFromDisk && onKeepDraft && (
)} {(onAcceptFile || onRejectFile) && (
{onAcceptFile && ( {isPreviewOnly && ( Accept/Reject is disabled while the file is missing on disk. )} )} {onRejectFile && ( {isPreviewOnly && ( Accept/Reject is disabled while the file is missing on disk. )} )}
)} {canRestore && restoreContent != null && ( Create/restore this file on disk from the preview )} {hasEdits && ( <> Discard all edits for this file Save file to disk {shortcutLabel('⌘ S', 'Ctrl+S')} )}
); };