@@ -468,8 +468,13 @@ function extractContentSnippet(noteId: string, searchTokens: string[], maxLength
468468 content = striptags ( content ) ;
469469 }
470470
471- // Normalize whitespace
472- content = content . replace ( / \s + / g, " " ) . trim ( ) ;
471+ // Normalize whitespace while preserving paragraph breaks
472+ // First, normalize multiple newlines to double newlines (paragraph breaks)
473+ content = content . replace ( / \n \s * \n / g, "\n\n" ) ;
474+ // Then normalize spaces within lines
475+ content = content . split ( '\n' ) . map ( line => line . replace ( / \s + / g, " " ) . trim ( ) ) . join ( '\n' ) ;
476+ // Finally trim the whole content
477+ content = content . trim ( ) ;
473478
474479 if ( ! content ) {
475480 return "" ;
@@ -495,21 +500,36 @@ function extractContentSnippet(noteId: string, searchTokens: string[], maxLength
495500 // Extract snippet
496501 let snippet = content . substring ( snippetStart , snippetStart + maxLength ) ;
497502
498- // Try to start/end at word boundaries
499- if ( snippetStart > 0 ) {
500- const firstSpace = snippet . indexOf ( " " ) ;
501- if ( firstSpace > 0 && firstSpace < 20 ) {
502- snippet = snippet . substring ( firstSpace + 1 ) ;
503+ // If snippet contains linebreaks, limit to max 4 lines and override character limit
504+ const lines = snippet . split ( '\n' ) ;
505+ if ( lines . length > 4 ) {
506+ snippet = lines . slice ( 0 , 4 ) . join ( '\n' ) ;
507+ // Add ellipsis if we truncated lines
508+ snippet = snippet + "..." ;
509+ } else if ( lines . length > 1 ) {
510+ // For multi-line snippets, just limit to 4 lines (keep existing snippet)
511+ snippet = lines . slice ( 0 , 4 ) . join ( '\n' ) ;
512+ if ( lines . length > 4 ) {
513+ snippet = snippet + "..." ;
503514 }
504- snippet = "..." + snippet ;
505- }
506-
507- if ( snippetStart + maxLength < content . length ) {
508- const lastSpace = snippet . lastIndexOf ( " " ) ;
509- if ( lastSpace > snippet . length - 20 ) {
510- snippet = snippet . substring ( 0 , lastSpace ) ;
515+ } else {
516+ // Single line content - apply original word boundary logic
517+ // Try to start/end at word boundaries
518+ if ( snippetStart > 0 ) {
519+ const firstSpace = snippet . search ( / \s / ) ;
520+ if ( firstSpace > 0 && firstSpace < 20 ) {
521+ snippet = snippet . substring ( firstSpace + 1 ) ;
522+ }
523+ snippet = "..." + snippet ;
524+ }
525+
526+ if ( snippetStart + maxLength < content . length ) {
527+ const lastSpace = snippet . search ( / \s [ ^ \s ] * $ / ) ;
528+ if ( lastSpace > snippet . length - 20 && lastSpace > 0 ) {
529+ snippet = snippet . substring ( 0 , lastSpace ) ;
530+ }
531+ snippet = snippet + "..." ;
511532 }
512- snippet = snippet + "..." ;
513533 }
514534
515535 return snippet ;
@@ -574,7 +594,10 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens
574594
575595 // Initialize highlighted content snippet
576596 if ( result . contentSnippet ) {
577- result . highlightedContentSnippet = escapeHtml ( result . contentSnippet ) . replace ( / [ < { } ] / g, "" ) ;
597+ // Escape HTML but preserve newlines for later conversion to <br>
598+ result . highlightedContentSnippet = escapeHtml ( result . contentSnippet ) ;
599+ // Remove any stray < { } that might interfere with our highlighting markers
600+ result . highlightedContentSnippet = result . highlightedContentSnippet . replace ( / [ < { } ] / g, "" ) ;
578601 }
579602 }
580603
@@ -621,7 +644,10 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens
621644 }
622645
623646 if ( result . highlightedContentSnippet ) {
647+ // Replace highlighting markers with HTML tags
624648 result . highlightedContentSnippet = result . highlightedContentSnippet . replace ( / { / g, "<b>" ) . replace ( / } / g, "</b>" ) ;
649+ // Convert newlines to <br> tags for HTML display
650+ result . highlightedContentSnippet = result . highlightedContentSnippet . replace ( / \n / g, "<br>" ) ;
625651 }
626652 }
627653}
0 commit comments