'use client'; import { memo, useMemo, useState } from 'react'; import { DiffModeEnum, DiffView } from '@git-diff-view/react'; import { IconArrowBackUp, IconCopy, IconLayoutColumns, IconLayoutRows } from '@tabler/icons-react'; import { useTheme } from 'next-themes'; import { Badge } from '@kandev/ui/badge'; import { Button } from '@kandev/ui/button'; import { Tooltip, TooltipContent, TooltipTrigger } from '@kandev/ui/tooltip'; import { cn } from '@/lib/utils'; import { getLocalStorage, setLocalStorage } from '@/lib/local-storage'; import { useAppStore } from '@/components/state-provider'; import { useSessionGitStatus } from '@/hooks/use-session-git-status'; type TaskChangesPanelProps = { selectedDiffPath: string | null; onClearSelected: () => void; }; const DEFAULT_DIFF_MODE: 'unified' | 'split' = 'unified'; const TaskChangesPanel = memo(function TaskChangesPanel({ selectedDiffPath, onClearSelected, }: TaskChangesPanelProps) { const [diffViewMode, setDiffViewMode] = useState<'unified' | 'split'>(() => getLocalStorage('task-diff-view-mode', DEFAULT_DIFF_MODE) ); const { resolvedTheme } = useTheme(); const activeSessionId = useAppStore((state) => state.tasks.activeSessionId); const gitStatus = useSessionGitStatus(activeSessionId); // Convert git status files to array for display const changedFiles = useMemo(() => { if (!gitStatus?.files || Object.keys(gitStatus.files).length === 0) { return []; } return Object.values(gitStatus.files); }, [gitStatus]); const diffTargets = useMemo( () => (selectedDiffPath ? [selectedDiffPath] : changedFiles.map((file) => file.path)), [selectedDiffPath, changedFiles] ); const diffModeEnum = diffViewMode === 'split' ? DiffModeEnum.Split : DiffModeEnum.Unified; const diffTheme = resolvedTheme === 'dark' ? 'dark' : 'light'; const selectedFile = selectedDiffPath && gitStatus ? gitStatus.files[selectedDiffPath] : null; const selectedDiffContent = selectedFile?.diff ?? ''; const isSingleDiffSelected = Boolean(selectedDiffPath && selectedFile); return (
{selectedDiffPath ?? 'All files'}
{selectedDiffPath && ( Show all changes )} Copy file contents Discard changes
Unified view Split view
{changedFiles.length === 0 ? (
No changes detected
) : (
{diffTargets.map((path) => { const file = gitStatus?.files?.[path]; if (!file || !file.diff) { return null; } // Detect file language from extension const ext = path.split('.').pop() || ''; const langMap: Record = { ts: 'typescript', tsx: 'tsx', js: 'javascript', jsx: 'jsx', py: 'python', go: 'go', rs: 'rust', java: 'java', cpp: 'cpp', c: 'c', css: 'css', html: 'html', json: 'json', md: 'markdown', yaml: 'yaml', yml: 'yaml', }; const fileLang = langMap[ext] || 'plaintext'; return (
{!selectedDiffPath && (
{path}
)}
); })}
)}
); }); export { TaskChangesPanel };