@@ -555,68 +555,85 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({
555555 Loading diff...
556556 </ div >
557557 ) : (
558- < div className = "flex min-h-0 flex-1 flex-row overflow-hidden @[800px]:flex-col" >
559- < div className = "order-1 flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden" >
560- { truncationWarning && (
561- < div className = "bg-warning/10 border-warning/30 text-warning mx-3 my-3 flex items-center gap-1.5 rounded border px-3 py-1.5 text-[10px] leading-[1.3] before:text-xs before:content-['⚠️']" >
562- { truncationWarning }
563- </ div >
564- ) }
558+ < div className = "flex min-h-0 flex-1 flex-col overflow-hidden" >
559+ { truncationWarning && (
560+ < div className = "bg-warning/10 border-warning/30 text-warning mx-3 my-3 flex items-center gap-1.5 rounded border px-3 py-1.5 text-[10px] leading-[1.3] before:text-xs before:content-['⚠️']" >
561+ { truncationWarning }
562+ </ div >
563+ ) }
564+
565+ { /* Search bar - always visible at top, not sticky */ }
566+ < div className = "border-border-light bg-separator border-b px-3 py-2" >
567+ < div className = "border-border-light bg-dark hover:border-border-gray focus-within:border-accent focus-within:hover:border-accent flex items-stretch overflow-hidden rounded border transition-[border-color] duration-150" >
568+ < input
569+ ref = { searchInputRef }
570+ type = "text"
571+ placeholder = { `Search in files and hunks... (${ formatKeybind ( KEYBINDS . FOCUS_REVIEW_SEARCH ) } )` }
572+ value = { searchState . input }
573+ onChange = { ( e ) => setSearchState ( { ...searchState , input : e . target . value } ) }
574+ className = "text-foreground placeholder:text-dim focus:bg-separator flex h-full flex-1 items-center border-none bg-transparent px-2.5 py-1.5 font-sans text-xs leading-[1.4] outline-none"
575+ />
576+ < TooltipWrapper inline >
577+ < button
578+ className = { cn (
579+ "py-1.5 px-2.5 border-none border-l border-light text-[11px] font-monospace font-semibold leading-[1.4] cursor-pointer outline-none transition-all duration-150 whitespace-nowrap flex items-center h-full" ,
580+ searchState . useRegex
581+ ? "bg-review-bg-blue text-accent-light shadow-[inset_0_0_0_1px_rgba(77,184,255,0.4)] hover:bg-review-bg-info hover:text-accent-light"
582+ : "bg-transparent text-subtle hover:bg-separator hover:text-foreground" ,
583+ "active:translate-y-px"
584+ ) }
585+ onClick = { ( ) =>
586+ setSearchState ( { ...searchState , useRegex : ! searchState . useRegex } )
587+ }
588+ >
589+ .*
590+ </ button >
591+ < Tooltip position = "bottom" >
592+ { searchState . useRegex ? "Using regex search" : "Using substring search" }
593+ </ Tooltip >
594+ </ TooltipWrapper >
595+ < TooltipWrapper inline >
596+ < button
597+ className = { cn (
598+ "py-1.5 px-2.5 border-none border-l border-light text-[11px] font-monospace font-semibold leading-[1.4] cursor-pointer outline-none transition-all duration-150 whitespace-nowrap flex items-center h-full" ,
599+ searchState . matchCase
600+ ? "bg-review-bg-blue text-accent-light shadow-[inset_0_0_0_1px_rgba(77,184,255,0.4)] hover:bg-review-bg-info hover:text-accent-light"
601+ : "bg-transparent text-subtle hover:bg-separator hover:text-foreground" ,
602+ "active:translate-y-px"
603+ ) }
604+ onClick = { ( ) =>
605+ setSearchState ( { ...searchState , matchCase : ! searchState . matchCase } )
606+ }
607+ >
608+ Aa
609+ </ button >
610+ < Tooltip position = "bottom" >
611+ { searchState . matchCase
612+ ? "Match case (case-sensitive)"
613+ : "Ignore case (case-insensitive)" }
614+ </ Tooltip >
615+ </ TooltipWrapper >
616+ </ div >
617+ </ div >
565618
566- < div className = "border-border-light bg-separator border-b px-3 py-2" >
567- < div className = "border-border-light bg-dark hover:border-border-gray focus-within:border-accent focus-within:hover:border-accent flex items-stretch overflow-hidden rounded border transition-[border-color] duration-150" >
568- < input
569- ref = { searchInputRef }
570- type = "text"
571- placeholder = { `Search in files and hunks... (${ formatKeybind ( KEYBINDS . FOCUS_REVIEW_SEARCH ) } )` }
572- value = { searchState . input }
573- onChange = { ( e ) => setSearchState ( { ...searchState , input : e . target . value } ) }
574- className = "text-foreground placeholder:text-dim focus:bg-separator flex h-full flex-1 items-center border-none bg-transparent px-2.5 py-1.5 font-sans text-xs leading-[1.4] outline-none"
619+ { /* Single scrollable area containing both file tree and hunks */ }
620+ < div className = "flex min-h-0 flex-1 flex-col overflow-y-auto" >
621+ { /* FileTree at the top */ }
622+ { ( fileTree ?? isLoadingTree ) && (
623+ < div className = "border-border-light flex w-full flex-[0_0_auto] flex-col overflow-hidden border-b" >
624+ < FileTree
625+ root = { fileTree }
626+ selectedPath = { selectedFilePath }
627+ onSelectFile = { setSelectedFilePath }
628+ isLoading = { isLoadingTree }
629+ getFileReadStatus = { getFileReadStatus }
630+ workspaceId = { workspaceId }
575631 />
576- < TooltipWrapper inline >
577- < button
578- className = { cn (
579- "py-1.5 px-2.5 border-none border-l border-light text-[11px] font-monospace font-semibold leading-[1.4] cursor-pointer outline-none transition-all duration-150 whitespace-nowrap flex items-center h-full" ,
580- searchState . useRegex
581- ? "bg-review-bg-blue text-accent-light shadow-[inset_0_0_0_1px_rgba(77,184,255,0.4)] hover:bg-review-bg-info hover:text-accent-light"
582- : "bg-transparent text-subtle hover:bg-separator hover:text-foreground" ,
583- "active:translate-y-px"
584- ) }
585- onClick = { ( ) =>
586- setSearchState ( { ...searchState , useRegex : ! searchState . useRegex } )
587- }
588- >
589- .*
590- </ button >
591- < Tooltip position = "bottom" >
592- { searchState . useRegex ? "Using regex search" : "Using substring search" }
593- </ Tooltip >
594- </ TooltipWrapper >
595- < TooltipWrapper inline >
596- < button
597- className = { cn (
598- "py-1.5 px-2.5 border-none border-l border-light text-[11px] font-monospace font-semibold leading-[1.4] cursor-pointer outline-none transition-all duration-150 whitespace-nowrap flex items-center h-full" ,
599- searchState . matchCase
600- ? "bg-review-bg-blue text-accent-light shadow-[inset_0_0_0_1px_rgba(77,184,255,0.4)] hover:bg-review-bg-info hover:text-accent-light"
601- : "bg-transparent text-subtle hover:bg-separator hover:text-foreground" ,
602- "active:translate-y-px"
603- ) }
604- onClick = { ( ) =>
605- setSearchState ( { ...searchState , matchCase : ! searchState . matchCase } )
606- }
607- >
608- Aa
609- </ button >
610- < Tooltip position = "bottom" >
611- { searchState . matchCase
612- ? "Match case (case-sensitive)"
613- : "Ignore case (case-insensitive)" }
614- </ Tooltip >
615- </ TooltipWrapper >
616632 </ div >
617- </ div >
633+ ) }
618634
619- < div className = "min-h-0 flex-1 overflow-y-auto p-3" >
635+ { /* Hunks below the file tree */ }
636+ < div className = "flex flex-[0_0_auto] flex-col p-3" >
620637 { hunks . length === 0 ? (
621638 < div className = "text-muted flex flex-col items-center justify-start gap-3 px-6 pt-12 pb-6 text-center" >
622639 < div className = "text-foreground text-base font-medium" > No changes found</ div >
@@ -691,20 +708,6 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({
691708 ) }
692709 </ div >
693710 </ div >
694-
695- { /* FileTree positioning handled by CSS order property */ }
696- { ( fileTree ?? isLoadingTree ) && (
697- < div className = "border-border-light @[800px]:border-border-light order-2 flex min-h-0 w-80 shrink-0 flex-col overflow-hidden border-l @[800px]:order-0 @[800px]:h-auto @[800px]:w-full @[800px]:flex-[0_0_auto] @[800px]:border-b @[800px]:border-l-0" >
698- < FileTree
699- root = { fileTree }
700- selectedPath = { selectedFilePath }
701- onSelectFile = { setSelectedFilePath }
702- isLoading = { isLoadingTree }
703- getFileReadStatus = { getFileReadStatus }
704- workspaceId = { workspaceId }
705- />
706- </ div >
707- ) }
708711 </ div >
709712 ) }
710713 </ div >
0 commit comments