@@ -71,6 +71,12 @@ interface MemoryTreeNode {
7171 children ?: MemoryTreeNode [ ]
7272}
7373
74+ interface RankedSection {
75+ id : string
76+ title : string
77+ changed : boolean
78+ }
79+
7480function formatBytes ( size : number ) : string {
7581 if ( size < 1024 ) return `${ size } B`
7682 if ( size < 1024 * 1024 ) return `${ ( size / 1024 ) . toFixed ( 1 ) } KB`
@@ -393,6 +399,22 @@ function buildCatalogQuery(extraMarkdownDirs: string[], extraFilePaths: string[]
393399 return qs ? `?${ qs } ` : ''
394400}
395401
402+ function rankSections ( file : MemoryFile , modification ?: MemoryModification | null ) : RankedSection [ ] {
403+ const haystack = `${ modification ?. oldContent ?? '' } \n${ modification ?. newContent ?? '' } \n${ modification ?. generatedContent ?? '' } ` . toLowerCase ( )
404+ const ranked = file . sections . map ( section => ( {
405+ ...section ,
406+ changed : section . title . trim ( ) . length > 0 && haystack . includes ( section . title . toLowerCase ( ) ) ,
407+ } ) )
408+ ranked . sort ( ( a , b ) => {
409+ if ( a . changed !== b . changed ) return a . changed ? - 1 : 1
410+ return a . title . localeCompare ( b . title )
411+ } )
412+ if ( ! ranked . some ( section => section . changed ) && modification && ranked . length > 0 ) {
413+ ranked [ 0 ] = { ...ranked [ 0 ] , changed : true }
414+ }
415+ return ranked
416+ }
417+
396418export default function MemoryReviewPage ( ) {
397419 const { id } = useParams < { id : string } > ( )
398420 const navigate = useNavigate ( )
@@ -521,6 +543,14 @@ export default function MemoryReviewPage() {
521543 return payload . modifications . find ( mod => mod . fileId === selectedFileId ) ?? null
522544 } , [ payload , selectedFileId ] )
523545
546+ const modificationByFileId = useMemo ( ( ) => {
547+ const map = new Map < string , MemoryModification > ( )
548+ for ( const modification of payload ?. modifications ?? [ ] ) {
549+ map . set ( modification . fileId , modification )
550+ }
551+ return map
552+ } , [ payload ] )
553+
524554 const diffLines = useMemo ( ( ) => {
525555 if ( ! selectedModification ) return [ ]
526556 return computeSimpleDiff ( selectedModification . oldContent , selectedModification . newContent )
@@ -900,22 +930,46 @@ export default function MemoryReviewPage() {
900930 < span key = { cat } className = "text-[10px] px-1.5 py-0.5 rounded bg-zinc-100 dark:bg-zinc-800 text-zinc-500 dark:text-slate-400" > { cat } </ span >
901931 ) ) }
902932 </ div >
903- { file . sections . slice ( 0 , 8 ) . map ( sec => (
904- < button
905- key = { sec . id }
906- onClick = { ( ) => {
907- setSelectedFileId ( file . id )
908- setFocusedSectionTitle ( sec . title )
909- } }
910- className = { `block w-full text-left text-[11px] truncate ${
911- changedFileIds . has ( file . id )
912- ? 'text-amber-600 dark:text-amber-300 hover:text-amber-700 dark:hover:text-amber-200'
913- : 'text-zinc-500 dark:text-slate-400 hover:text-zinc-700 dark:hover:text-slate-200'
914- } `}
915- >
916- # { sec . title }
917- </ button >
918- ) ) }
933+ { ( ( ) => {
934+ const rankedSections = rankSections ( file , modificationByFileId . get ( file . id ) )
935+ const previewToggleId = `${ fileNodeId } :subtitle-preview`
936+ const previewExpanded = collapsedIds . has ( previewToggleId )
937+ const changedSections = rankedSections . filter ( section => section . changed )
938+ const unchangedSections = rankedSections . filter ( section => ! section . changed )
939+ const visibleSections = previewExpanded
940+ ? rankedSections . slice ( 0 , 8 )
941+ : [ ...changedSections . slice ( 0 , 4 ) , ...( changedSections . length === 0 ? unchangedSections . slice ( 0 , 1 ) : [ ] ) ]
942+ const hiddenCount = Math . max ( 0 , Math . min ( rankedSections . length , 8 ) - visibleSections . length )
943+ return (
944+ < >
945+ { visibleSections . map ( sec => (
946+ < button
947+ key = { sec . id }
948+ onClick = { ( ) => {
949+ setSelectedFileId ( file . id )
950+ setFocusedSectionTitle ( sec . title )
951+ } }
952+ className = { `block w-full text-left text-[11px] truncate ${
953+ sec . changed
954+ ? 'text-amber-600 dark:text-amber-300 hover:text-amber-700 dark:hover:text-amber-200 font-medium'
955+ : 'text-zinc-500 dark:text-slate-400 hover:text-zinc-700 dark:hover:text-slate-200'
956+ } `}
957+ >
958+ { sec . changed ? '# ' : '... ' }
959+ { sec . title }
960+ </ button >
961+ ) ) }
962+ { hiddenCount > 0 && (
963+ < button
964+ onClick = { ( ) => toggleCollapse ( previewToggleId ) }
965+ className = "block w-full text-left text-[11px] text-zinc-400 dark:text-slate-500 hover:text-zinc-700 dark:hover:text-slate-200"
966+ >
967+ { previewExpanded ? 'Fold subtitle preview' : `... unfold ${ hiddenCount } more subtitle${ hiddenCount > 1 ? 's' : '' } ` }
968+ </ button >
969+ ) }
970+ </ >
971+ )
972+ } ) ( ) }
919973 </ div >
920974 ) }
921975 </ div >
0 commit comments