mirror of
https://github.com/zadam/trilium.git
synced 2025-11-02 19:36:12 +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