@@ -247,6 +247,10 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
247247 const [ hasMore , setHasMore ] = useState ( true ) ;
248248 const [ error , setError ] = useState < string | null > ( null ) ;
249249
250+ const [ searchInput , setSearchInput ] = useState ( "" ) ;
251+ const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
252+ const searchRef = useRef < HTMLInputElement > ( null ) ;
253+
250254 const [ selectedCommit , setSelectedCommit ] = useState < CommitInfo | null > ( null ) ;
251255 const [ commitFiles , setCommitFiles ] = useState < CommitFile [ ] > ( [ ] ) ;
252256 const [ parents , setParents ] = useState < string [ ] > ( [ ] ) ;
@@ -265,7 +269,7 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
265269 if ( ! repoPath ) return ;
266270 skip === 0 ? setLoading ( true ) : setLoadingMore ( true ) ;
267271 try {
268- const result = await getHistory ( repoPath , filePath ?? "" , skip , PAGE_SIZE , allBranches ) ;
272+ const result = await getHistory ( repoPath , filePath ?? "" , skip , PAGE_SIZE , allBranches , searchQuery || undefined ) ;
269273 setCommits ( ( prev ) => skip === 0 ? result : [ ...prev , ...result ] ) ;
270274 setHasMore ( result . length === PAGE_SIZE ) ;
271275 setError ( null ) ;
@@ -275,7 +279,7 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
275279 setLoading ( false ) ;
276280 setLoadingMore ( false ) ;
277281 }
278- } , [ repoPath , filePath , allBranches ] ) ;
282+ } , [ repoPath , filePath , allBranches , searchQuery ] ) ;
279283
280284 useEffect ( ( ) => {
281285 setSelectedCommit ( null ) ;
@@ -334,8 +338,8 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
334338 ) !== "/" ;
335339
336340 const graphRows = useMemo (
337- ( ) => isPathFiltered ? computeLinearLayout ( commits . length ) : computeGraphLayout ( commits ) ,
338- [ commits , isPathFiltered ]
341+ ( ) => ( isPathFiltered || ! ! searchQuery ) ? computeLinearLayout ( commits . length ) : computeGraphLayout ( commits ) ,
342+ [ commits , isPathFiltered , searchQuery ]
339343 ) ;
340344
341345 // ── render ────────────────────────────────────────────────────────────────
@@ -382,6 +386,48 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
382386 All Branches
383387 </ label >
384388 </ div >
389+ { /* ── Search bar ── */ }
390+ < div className = "flex items-center gap-2 px-3 py-1 border-b border-gray-200 dark:border-gray-700 shrink-0" >
391+ < button
392+ onClick = { ( ) => setSearchQuery ( searchInput ) }
393+ className = "text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 shrink-0"
394+ title = "Search"
395+ >
396+ < svg width = "12" height = "12" viewBox = "0 0 16 16" fill = "currentColor" >
397+ < path d = "M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z" />
398+ </ svg >
399+ </ button >
400+ < input
401+ ref = { searchRef }
402+ type = "text"
403+ value = { searchInput }
404+ onChange = { ( e ) => setSearchInput ( e . target . value ) }
405+ onKeyDown = { ( e ) => {
406+ if ( e . key === "Enter" ) setSearchQuery ( searchInput ) ;
407+ if ( e . key === "Escape" ) { setSearchInput ( "" ) ; setSearchQuery ( "" ) ; }
408+ } }
409+ placeholder = "Search commits..."
410+ className = "flex-1 min-w-0 bg-transparent text-xs text-gray-700 dark:text-gray-300 placeholder-gray-400 outline-none"
411+ />
412+ { searchQuery && (
413+ loading ? (
414+ < span className = "text-xs text-gray-400 shrink-0" > ...</ span >
415+ ) : (
416+ < span className = "text-xs text-gray-400 shrink-0" > { commits . length } { hasMore ? "+" : "" } results</ span >
417+ )
418+ ) }
419+ { searchInput && (
420+ < button
421+ onClick = { ( ) => { setSearchInput ( "" ) ; setSearchQuery ( "" ) ; } }
422+ className = "shrink-0 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200"
423+ title = "Clear search"
424+ >
425+ < svg width = "12" height = "12" viewBox = "0 0 16 16" fill = "currentColor" >
426+ < path d = "M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z" />
427+ </ svg >
428+ </ button >
429+ ) }
430+ </ div >
385431 { /* ── Top: commit list ── */ }
386432 < div className = "flex-[3] overflow-y-auto border-b border-gray-200 dark:border-gray-700" >
387433 { commits . map ( ( commit , idx ) => {
0 commit comments