@@ -120,6 +120,7 @@ const MainApp: React.FC = () => {
120120 const [ contextMenu , setContextMenu ] = useState < { isOpen : boolean ; position : { x : number , y : number } , items : MenuItem [ ] } > ( { isOpen : false , position : { x : 0 , y : 0 } , items : [ ] } ) ;
121121 const [ isDraggingFile , setIsDraggingFile ] = useState ( false ) ;
122122 const [ formatTrigger , setFormatTrigger ] = useState ( 0 ) ;
123+ const [ bodySearchMatches , setBodySearchMatches ] = useState < Map < string , string > > ( new Map ( ) ) ;
123124
124125
125126 const isSidebarResizing = useRef ( false ) ;
@@ -172,9 +173,20 @@ const MainApp: React.FC = () => {
172173 } , [ theme , settings . markdownCodeBlockBackgroundLight , settings . markdownCodeBlockBackgroundDark , settingsLoaded ] ) ;
173174
174175
176+ const itemsWithSearchMetadata = useMemo ( ( ) => {
177+ const trimmed = searchTerm . trim ( ) ;
178+ if ( ! trimmed || bodySearchMatches . size === 0 ) {
179+ return items ;
180+ }
181+ return items . map ( item => {
182+ const snippet = bodySearchMatches . get ( item . id ) ;
183+ return snippet ? { ...item , searchSnippet : snippet } : item ;
184+ } ) ;
185+ } , [ items , bodySearchMatches , searchTerm ] ) ;
186+
175187 const activeNode = useMemo ( ( ) => {
176- return items . find ( p => p . id === activeNodeId ) || null ;
177- } , [ items , activeNodeId ] ) ;
188+ return itemsWithSearchMetadata . find ( p => p . id === activeNodeId ) || null ;
189+ } , [ itemsWithSearchMetadata , activeNodeId ] ) ;
178190
179191 const activeTemplate = useMemo ( ( ) => {
180192 return templates . find ( t => t . template_id === activeTemplateId ) || null ;
@@ -185,35 +197,71 @@ const MainApp: React.FC = () => {
185197 } , [ activeNode ] ) ;
186198
187199
200+ useEffect ( ( ) => {
201+ const term = searchTerm . trim ( ) ;
202+ if ( ! term ) {
203+ setBodySearchMatches ( new Map ( ) ) ;
204+ return ;
205+ }
206+
207+ let isCancelled = false ;
208+
209+ repository . searchDocumentsByBody ( term , 200 )
210+ . then ( results => {
211+ if ( ! isCancelled ) {
212+ setBodySearchMatches ( new Map ( results . map ( result => [ result . nodeId , result . snippet ] ) ) ) ;
213+ }
214+ } )
215+ . catch ( error => {
216+ if ( ! isCancelled ) {
217+ console . error ( 'Failed to search document bodies:' , error ) ;
218+ setBodySearchMatches ( new Map ( ) ) ;
219+ }
220+ } ) ;
221+
222+ return ( ) => {
223+ isCancelled = true ;
224+ } ;
225+ } , [ searchTerm ] ) ;
226+
188227 const { documentTree, navigableItems } = useMemo ( ( ) => {
189- let itemsToBuildFrom = items ;
228+ let itemsToBuildFrom = itemsWithSearchMetadata ;
190229 if ( searchTerm . trim ( ) ) {
191230 const lowerCaseSearchTerm = searchTerm . toLowerCase ( ) ;
192231 const visibleIds = new Set < string > ( ) ;
193- const originalItemsById : Map < string , DocumentOrFolder > = new Map ( items . map ( i => [ i . id , i ] ) ) ;
232+ const originalItemsById : Map < string , DocumentOrFolder > = new Map ( itemsWithSearchMetadata . map ( i => [ i . id , i ] ) ) ;
194233 const getAncestors = ( itemId : string ) => {
195234 let current = originalItemsById . get ( itemId ) ;
196235 while ( current && current . parentId ) {
197- visibleIds . add ( current . parentId ) ;
198- current = originalItemsById . get ( current . parentId ) ;
236+ visibleIds . add ( current . parentId ) ;
237+ current = originalItemsById . get ( current . parentId ) ;
199238 }
200239 } ;
201240 const getDescendantIdsRecursive = ( itemId : string ) : Set < string > => {
202241 const descendantIds = new Set < string > ( ) ;
203242 const findChildren = ( parentId : string ) => {
204- items . forEach ( p => { if ( p . parentId === parentId ) { descendantIds . add ( p . id ) ; if ( p . type === 'folder' ) findChildren ( p . id ) ; } } ) ;
243+ itemsWithSearchMetadata . forEach ( p => {
244+ if ( p . parentId === parentId ) {
245+ descendantIds . add ( p . id ) ;
246+ if ( p . type === 'folder' ) findChildren ( p . id ) ;
247+ }
248+ } ) ;
205249 } ;
206250 findChildren ( itemId ) ;
207251 return descendantIds ;
208252 } ;
209- items . forEach ( item => {
210- if ( item . title . toLowerCase ( ) . includes ( lowerCaseSearchTerm ) ) {
253+ itemsWithSearchMetadata . forEach ( item => {
254+ const titleMatch = item . title . toLowerCase ( ) . includes ( lowerCaseSearchTerm ) ;
255+ const bodyMatch = Boolean ( item . searchSnippet ) ;
256+ if ( titleMatch || bodyMatch ) {
211257 visibleIds . add ( item . id ) ;
212258 getAncestors ( item . id ) ;
213- if ( item . type === 'folder' ) getDescendantIdsRecursive ( item . id ) . forEach ( id => visibleIds . add ( id ) ) ;
259+ if ( titleMatch && item . type === 'folder' ) {
260+ getDescendantIdsRecursive ( item . id ) . forEach ( id => visibleIds . add ( id ) ) ;
261+ }
214262 }
215263 } ) ;
216- itemsToBuildFrom = items . filter ( item => visibleIds . has ( item . id ) ) ;
264+ itemsToBuildFrom = itemsWithSearchMetadata . filter ( item => visibleIds . has ( item . id ) ) ;
217265 }
218266 const itemsById = new Map < string , DocumentNode > ( itemsToBuildFrom . map ( p => [ p . id , { ...p , children : [ ] } ] ) ) ;
219267 const rootNodes : DocumentNode [ ] = [ ] ;
@@ -225,27 +273,27 @@ const MainApp: React.FC = () => {
225273 rootNodes . push ( node ) ;
226274 }
227275 }
228-
276+
229277 const finalTree = rootNodes ;
230278
231- const displayExpandedIds = searchTerm . trim ( )
232- ? new Set ( itemsToBuildFrom . filter ( i => i . type === 'folder' ) . map ( i => i . id ) )
279+ const displayExpandedIds = searchTerm . trim ( )
280+ ? new Set ( itemsToBuildFrom . filter ( i => i . type === 'folder' ) . map ( i => i . id ) )
233281 : expandedFolderIds ;
234282
235283 const flatList : NavigableItem [ ] = [ ] ;
236284 const flatten = ( nodes : DocumentNode [ ] ) => {
237- for ( const node of nodes ) {
238- flatList . push ( { id : node . id , type : node . type , parentId : node . parentId } ) ;
239- if ( node . type === 'folder' && displayExpandedIds . has ( node . id ) ) {
240- flatten ( node . children ) ;
285+ for ( const node of nodes ) {
286+ flatList . push ( { id : node . id , type : node . type , parentId : node . parentId } ) ;
287+ if ( node . type === 'folder' && displayExpandedIds . has ( node . id ) ) {
288+ flatten ( node . children ) ;
289+ }
241290 }
242- }
243291 } ;
244292 flatten ( finalTree ) ;
245293 templates . forEach ( t => flatList . push ( { id : t . template_id , type : 'template' , parentId : null } ) ) ;
246294
247295 return { documentTree : finalTree , navigableItems : flatList } ;
248- } , [ items , templates , searchTerm , expandedFolderIds ] ) ;
296+ } , [ itemsWithSearchMetadata , templates , searchTerm , expandedFolderIds ] ) ;
249297
250298 useEffect ( ( ) => {
251299 if ( window . electronAPI ?. getAppVersion ) {
@@ -1120,8 +1168,8 @@ const MainApp: React.FC = () => {
11201168 style = { { width : `${ sidebarWidth } px` } }
11211169 className = "bg-secondary border-r border-border-color flex flex-col flex-shrink-0"
11221170 >
1123- < Sidebar
1124- documents = { items }
1171+ < Sidebar
1172+ documents = { itemsWithSearchMetadata }
11251173 documentTree = { documentTree }
11261174 navigableItems = { navigableItems }
11271175 selectedIds = { selectedIds }
0 commit comments