source dump of claude code
at main 110 lines 2.8 kB view raw
1import type { StructuredPatchHunk } from 'diff' 2import { useEffect, useMemo, useState } from 'react' 3import { 4 fetchGitDiff, 5 fetchGitDiffHunks, 6 type GitDiffResult, 7 type GitDiffStats, 8} from '../utils/gitDiff.js' 9 10const MAX_LINES_PER_FILE = 400 11 12export type DiffFile = { 13 path: string 14 linesAdded: number 15 linesRemoved: number 16 isBinary: boolean 17 isLargeFile: boolean 18 isTruncated: boolean 19 isNewFile?: boolean 20 isUntracked?: boolean 21} 22 23export type DiffData = { 24 stats: GitDiffStats | null 25 files: DiffFile[] 26 hunks: Map<string, StructuredPatchHunk[]> 27 loading: boolean 28} 29 30/** 31 * Hook to fetch current git diff data on demand. 32 * Fetches both stats and hunks when component mounts. 33 */ 34export function useDiffData(): DiffData { 35 const [diffResult, setDiffResult] = useState<GitDiffResult | null>(null) 36 const [hunks, setHunks] = useState<Map<string, StructuredPatchHunk[]>>( 37 new Map(), 38 ) 39 const [loading, setLoading] = useState(true) 40 41 // Fetch diff data on mount 42 useEffect(() => { 43 let cancelled = false 44 45 async function loadDiffData() { 46 try { 47 // Fetch both stats and hunks 48 const [statsResult, hunksResult] = await Promise.all([ 49 fetchGitDiff(), 50 fetchGitDiffHunks(), 51 ]) 52 53 if (!cancelled) { 54 setDiffResult(statsResult) 55 setHunks(hunksResult) 56 setLoading(false) 57 } 58 } catch (_error) { 59 if (!cancelled) { 60 setDiffResult(null) 61 setHunks(new Map()) 62 setLoading(false) 63 } 64 } 65 } 66 67 void loadDiffData() 68 69 return () => { 70 cancelled = true 71 } 72 }, []) 73 74 return useMemo(() => { 75 if (!diffResult) { 76 return { stats: null, files: [], hunks: new Map(), loading } 77 } 78 79 const { stats, perFileStats } = diffResult 80 const files: DiffFile[] = [] 81 82 // Iterate over perFileStats to get all files including large/skipped ones 83 for (const [path, fileStats] of perFileStats) { 84 const fileHunks = hunks.get(path) 85 const isUntracked = fileStats.isUntracked ?? false 86 87 // Detect large file (in perFileStats but not in hunks, and not binary/untracked) 88 const isLargeFile = !fileStats.isBinary && !isUntracked && !fileHunks 89 90 // Detect truncated file (total > limit means we truncated) 91 const totalLines = fileStats.added + fileStats.removed 92 const isTruncated = 93 !isLargeFile && !fileStats.isBinary && totalLines > MAX_LINES_PER_FILE 94 95 files.push({ 96 path, 97 linesAdded: fileStats.added, 98 linesRemoved: fileStats.removed, 99 isBinary: fileStats.isBinary, 100 isLargeFile, 101 isTruncated, 102 isUntracked, 103 }) 104 } 105 106 files.sort((a, b) => a.path.localeCompare(b.path)) 107 108 return { stats, files, hunks, loading: false } 109 }, [diffResult, hunks, loading]) 110}