mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	feat(jump_to): don't show content context in results, go faster 🚄
This commit is contained in:
		@@ -30,8 +30,6 @@ export interface Suggestion {
 | 
			
		||||
    notePathTitle?: string;
 | 
			
		||||
    notePath?: string;
 | 
			
		||||
    highlightedNotePathTitle?: string;
 | 
			
		||||
    contentSnippet?: string;
 | 
			
		||||
    highlightedContentSnippet?: string;
 | 
			
		||||
    attributeSnippet?: string;
 | 
			
		||||
    highlightedAttributeSnippet?: string;
 | 
			
		||||
    action?: string | "create-note" | "search-notes" | "external-link" | "command";
 | 
			
		||||
@@ -329,21 +327,16 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
 | 
			
		||||
                            return html;
 | 
			
		||||
                        }
 | 
			
		||||
                        
 | 
			
		||||
                        // For note suggestions, match Quick Search structure exactly
 | 
			
		||||
                        // For note suggestions, match Quick Search structure
 | 
			
		||||
                        // Title row with icon
 | 
			
		||||
                        let html = `<div style="display: flex; align-items: center; gap: 6px;">`;
 | 
			
		||||
                        html += `<span class="${suggestion.icon ?? "bx bx-note"}" style="flex-shrink: 0;"></span>`;
 | 
			
		||||
                        html += `<span class="search-result-title" style="flex: 1;">${suggestion.highlightedNotePathTitle || ''}</span>`;
 | 
			
		||||
                        html += `</div>`;
 | 
			
		||||
                        
 | 
			
		||||
                        // Add attribute snippet if available
 | 
			
		||||
                        // Add attribute snippet if available (inline display)
 | 
			
		||||
                        if (suggestion.highlightedAttributeSnippet && suggestion.highlightedAttributeSnippet.trim()) {
 | 
			
		||||
                            html += `<div class="search-result-attributes" style="margin-left: 20px; margin-top: 2px;">${suggestion.highlightedAttributeSnippet}</div>`;
 | 
			
		||||
                        }
 | 
			
		||||
                        
 | 
			
		||||
                        // Add content snippet if available
 | 
			
		||||
                        if (suggestion.highlightedContentSnippet && suggestion.highlightedContentSnippet.trim()) {
 | 
			
		||||
                            html += `<div class="search-result-content" style="margin-left: 20px; margin-top: 2px;">${suggestion.highlightedContentSnippet}</div>`;
 | 
			
		||||
                            html += `<div class="search-result-attributes" style="margin-left: 20px; margin-top: 2px; color: var(--muted-text-color); font-size: 0.9em;">${suggestion.highlightedAttributeSnippet}</div>`;
 | 
			
		||||
                        }
 | 
			
		||||
                        
 | 
			
		||||
                        return html;
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,10 @@
 | 
			
		||||
 * with different styling and layout requirements.
 | 
			
		||||
 * 
 | 
			
		||||
 * SECURITY NOTE: HTML Snippet Handling
 | 
			
		||||
 * The highlighted snippet fields (highlightedContentSnippet, highlightedAttributeSnippet) contain
 | 
			
		||||
 * The highlighted snippet fields (highlightedAttributeSnippet) contain
 | 
			
		||||
 * pre-sanitized HTML from the server. The server-side processing:
 | 
			
		||||
 * 1. Escapes all HTML using the escape-html library
 | 
			
		||||
 * 2. Adds safe HTML tags for display: <b> for search term highlighting, <br> for line breaks
 | 
			
		||||
 * 2. Adds safe HTML tags for display: <b> for search term highlighting
 | 
			
		||||
 * 3. See apps/server/src/services/search/services/search.ts for implementation
 | 
			
		||||
 * 
 | 
			
		||||
 * This means the HTML snippets can be safely inserted without additional escaping on the client side.
 | 
			
		||||
@@ -41,7 +41,7 @@ export function createSearchResultHtml(result: Suggestion): string {
 | 
			
		||||
        return html;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Default: render as note result with snippets
 | 
			
		||||
    // Default: render as note result
 | 
			
		||||
    // Wrap everything in a flex column container
 | 
			
		||||
    let itemHtml = `<div style="display: flex; flex-direction: column; gap: 2px;">`;
 | 
			
		||||
    
 | 
			
		||||
@@ -51,14 +51,9 @@ export function createSearchResultHtml(result: Suggestion): string {
 | 
			
		||||
    itemHtml += `<span class="search-result-title" style="flex: 1;">${result.highlightedNotePathTitle || result.notePathTitle || ''}</span>`;
 | 
			
		||||
    itemHtml += `</div>`;
 | 
			
		||||
    
 | 
			
		||||
    // Add attribute snippet if available
 | 
			
		||||
    // Add attribute snippet if available (inline display)
 | 
			
		||||
    if (result.highlightedAttributeSnippet && result.highlightedAttributeSnippet.trim()) {
 | 
			
		||||
        itemHtml += `<div class="search-result-attributes" style="margin-left: 20px;">${result.highlightedAttributeSnippet}</div>`;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Add content snippet if available
 | 
			
		||||
    if (result.highlightedContentSnippet && result.highlightedContentSnippet.trim()) {
 | 
			
		||||
        itemHtml += `<div class="search-result-content" style="margin-left: 20px;">${result.highlightedContentSnippet}</div>`;
 | 
			
		||||
        itemHtml += `<div class="search-result-attributes" style="margin-left: 20px; color: var(--muted-text-color); font-size: 0.9em;">${result.highlightedAttributeSnippet}</div>`;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    itemHtml += `</div>`;
 | 
			
		||||
 
 | 
			
		||||
@@ -92,8 +92,6 @@ interface QuickSearchResponse {
 | 
			
		||||
        noteTitle: string;
 | 
			
		||||
        notePathTitle: string;
 | 
			
		||||
        highlightedNotePathTitle: string;
 | 
			
		||||
        contentSnippet?: string;
 | 
			
		||||
        highlightedContentSnippet?: string;
 | 
			
		||||
        attributeSnippet?: string;
 | 
			
		||||
        highlightedAttributeSnippet?: string;
 | 
			
		||||
        icon: string;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,6 @@ class SearchResult {
 | 
			
		||||
    score: number;
 | 
			
		||||
    notePathTitle: string;
 | 
			
		||||
    highlightedNotePathTitle?: string;
 | 
			
		||||
    contentSnippet?: string;
 | 
			
		||||
    highlightedContentSnippet?: string;
 | 
			
		||||
    attributeSnippet?: string;
 | 
			
		||||
    highlightedAttributeSnippet?: string;
 | 
			
		||||
    private fuzzyScore: number; // Track fuzzy score separately
 | 
			
		||||
 
 | 
			
		||||
@@ -607,7 +607,7 @@ function extractAttributeSnippet(noteId: string, searchTokens: string[], maxLeng
 | 
			
		||||
            return "";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Limit to 4 lines maximum, similar to content snippet logic
 | 
			
		||||
        // Display attributes inline, separated by spaces
 | 
			
		||||
        const lines: string[] = [];
 | 
			
		||||
        for (const attr of matchingAttributes.slice(0, 4)) {
 | 
			
		||||
            let line = "";
 | 
			
		||||
@@ -625,7 +625,7 @@ function extractAttributeSnippet(noteId: string, searchTokens: string[], maxLeng
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let snippet = lines.join('\n');
 | 
			
		||||
        let snippet = lines.join(' '); // Join with spaces instead of newlines
 | 
			
		||||
        
 | 
			
		||||
        // Apply length limit while preserving line structure
 | 
			
		||||
        if (snippet.length > maxLength) {
 | 
			
		||||
@@ -665,9 +665,8 @@ function searchNotesForAutocomplete(query: string, fastSearch: boolean = true) {
 | 
			
		||||
 | 
			
		||||
    const trimmed = allSearchResults.slice(0, 200);
 | 
			
		||||
 | 
			
		||||
    // Extract content and attribute snippets
 | 
			
		||||
    // Extract attribute snippets only (content snippets removed for performance)
 | 
			
		||||
    for (const result of trimmed) {
 | 
			
		||||
        result.contentSnippet = extractContentSnippet(result.noteId, searchContext.highlightedTokens);
 | 
			
		||||
        result.attributeSnippet = extractAttributeSnippet(result.noteId, searchContext.highlightedTokens);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -680,8 +679,6 @@ function searchNotesForAutocomplete(query: string, fastSearch: boolean = true) {
 | 
			
		||||
            noteTitle: title,
 | 
			
		||||
            notePathTitle: result.notePathTitle,
 | 
			
		||||
            highlightedNotePathTitle: result.highlightedNotePathTitle,
 | 
			
		||||
            contentSnippet: result.contentSnippet,
 | 
			
		||||
            highlightedContentSnippet: result.highlightedContentSnippet,
 | 
			
		||||
            attributeSnippet: result.attributeSnippet,
 | 
			
		||||
            highlightedAttributeSnippet: result.highlightedAttributeSnippet,
 | 
			
		||||
            icon: icon ?? "bx bx-note"
 | 
			
		||||
@@ -707,17 +704,9 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens
 | 
			
		||||
    for (const result of searchResults) {
 | 
			
		||||
        result.highlightedNotePathTitle = result.notePathTitle.replace(/[<{}]/g, "");
 | 
			
		||||
        
 | 
			
		||||
        // Initialize highlighted content snippet
 | 
			
		||||
        if (result.contentSnippet) {
 | 
			
		||||
            // Escape HTML but preserve newlines for later conversion to <br>
 | 
			
		||||
            result.highlightedContentSnippet = escapeHtml(result.contentSnippet);
 | 
			
		||||
            // Remove any stray < { } that might interfere with our highlighting markers
 | 
			
		||||
            result.highlightedContentSnippet = result.highlightedContentSnippet.replace(/[<{}]/g, "");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Initialize highlighted attribute snippet
 | 
			
		||||
        if (result.attributeSnippet) {
 | 
			
		||||
            // Escape HTML but preserve newlines for later conversion to <br>
 | 
			
		||||
            // Escape HTML
 | 
			
		||||
            result.highlightedAttributeSnippet = escapeHtml(result.attributeSnippet);
 | 
			
		||||
            // Remove any stray < { } that might interfere with our highlighting markers
 | 
			
		||||
            result.highlightedAttributeSnippet = result.highlightedAttributeSnippet.replace(/[<{}]/g, "");
 | 
			
		||||
@@ -749,16 +738,6 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Highlight in content snippet
 | 
			
		||||
            if (result.highlightedContentSnippet) {
 | 
			
		||||
                const contentRegex = new RegExp(escapeRegExp(token), "gi");
 | 
			
		||||
                while ((match = contentRegex.exec(normalizeString(result.highlightedContentSnippet))) !== null) {
 | 
			
		||||
                    result.highlightedContentSnippet = wrapText(result.highlightedContentSnippet, match.index, token.length, "{", "}");
 | 
			
		||||
                    // 2 characters are added, so we need to adjust the index
 | 
			
		||||
                    contentRegex.lastIndex += 2;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Highlight in attribute snippet
 | 
			
		||||
            if (result.highlightedAttributeSnippet) {
 | 
			
		||||
                const attributeRegex = new RegExp(escapeRegExp(token), "gi");
 | 
			
		||||
@@ -776,18 +755,10 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens
 | 
			
		||||
            result.highlightedNotePathTitle = result.highlightedNotePathTitle.replace(/{/g, "<b>").replace(/}/g, "</b>");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (result.highlightedContentSnippet) {
 | 
			
		||||
            // Replace highlighting markers with HTML tags
 | 
			
		||||
            result.highlightedContentSnippet = result.highlightedContentSnippet.replace(/{/g, "<b>").replace(/}/g, "</b>");
 | 
			
		||||
            // Convert newlines to <br> tags for HTML display
 | 
			
		||||
            result.highlightedContentSnippet = result.highlightedContentSnippet.replace(/\n/g, "<br>");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (result.highlightedAttributeSnippet) {
 | 
			
		||||
            // Replace highlighting markers with HTML tags
 | 
			
		||||
            result.highlightedAttributeSnippet = result.highlightedAttributeSnippet.replace(/{/g, "<b>").replace(/}/g, "</b>");
 | 
			
		||||
            // Convert newlines to <br> tags for HTML display
 | 
			
		||||
            result.highlightedAttributeSnippet = result.highlightedAttributeSnippet.replace(/\n/g, "<br>");
 | 
			
		||||
            // Keep inline display - no conversion of newlines needed
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user