Merge pull request #173 from ricky-davis/feat/better-search
feat: debounce search input
This commit is contained in:
commit
3495c0d9ee
|
|
@ -3,6 +3,7 @@
|
|||
// Configuration constants
|
||||
const CONFIG = {
|
||||
AUTOSAVE_DELAY: 1000, // ms - Delay before triggering autosave
|
||||
SEARCH_DEBOUNCE_DELAY: 1000, // ms - Delay before running note search while typing
|
||||
SAVE_INDICATOR_DURATION: 2000, // ms - How long to show "saved" indicator
|
||||
SCROLL_SYNC_DELAY: 50, // ms - Delay to prevent scroll sync interference
|
||||
SCROLL_SYNC_MAX_RETRIES: 10, // Maximum attempts to find editor/preview elements
|
||||
|
|
@ -227,6 +228,10 @@ function noteApp() {
|
|||
selectedTags: [],
|
||||
tagsExpanded: false,
|
||||
tagReloadTimeout: null, // For debouncing tag reloads
|
||||
|
||||
// Search state
|
||||
searchDebounceTimeout: null,
|
||||
isSearching: false,
|
||||
|
||||
// Outline (TOC) state
|
||||
outline: [], // [{level: 1, text: 'Heading', slug: 'heading'}, ...]
|
||||
|
|
@ -1431,6 +1436,7 @@ function noteApp() {
|
|||
|
||||
// Case 1: No filters at all → show full folder tree
|
||||
if (!hasTextSearch && !hasTagFilter) {
|
||||
this.isSearching = false;
|
||||
this.searchResults = [];
|
||||
this.currentSearchHighlight = '';
|
||||
this.clearSearchHighlights();
|
||||
|
|
@ -1440,6 +1446,7 @@ function noteApp() {
|
|||
|
||||
// Case 2: Only tag filter → convert to flat list of matching notes
|
||||
if (hasTagFilter && !hasTextSearch) {
|
||||
this.isSearching = false;
|
||||
this.searchResults = this.notes.filter(note =>
|
||||
note.type === 'note' && this.noteMatchesTags(note)
|
||||
);
|
||||
|
|
@ -1450,6 +1457,7 @@ function noteApp() {
|
|||
|
||||
// Case 3: Text search (with or without tag filter)
|
||||
if (hasTextSearch) {
|
||||
this.isSearching = true;
|
||||
try {
|
||||
const response = await fetch(`/api/search?q=${encodeURIComponent(this.searchQuery)}`);
|
||||
const data = await response.json();
|
||||
|
|
@ -1474,6 +1482,9 @@ function noteApp() {
|
|||
}
|
||||
} catch (error) {
|
||||
console.error('Search failed:', error);
|
||||
this.searchResults = [];
|
||||
} finally {
|
||||
this.isSearching = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -3842,6 +3853,26 @@ function noteApp() {
|
|||
},
|
||||
|
||||
// Search notes
|
||||
debouncedSearchNotes() {
|
||||
if (this.searchDebounceTimeout) {
|
||||
clearTimeout(this.searchDebounceTimeout);
|
||||
}
|
||||
|
||||
const hasTextSearch = this.searchQuery.trim().length > 0;
|
||||
if (!hasTextSearch) {
|
||||
this.isSearching = false;
|
||||
this.searchNotes();
|
||||
return;
|
||||
}
|
||||
|
||||
this.isSearching = true;
|
||||
this.searchResults = [];
|
||||
|
||||
this.searchDebounceTimeout = setTimeout(() => {
|
||||
this.searchNotes();
|
||||
}, CONFIG.SEARCH_DEBOUNCE_DELAY);
|
||||
},
|
||||
|
||||
// Search notes by text (calls unified filter logic)
|
||||
async searchNotes() {
|
||||
await this.applyFilters();
|
||||
|
|
|
|||
|
|
@ -1621,7 +1621,7 @@
|
|||
<input
|
||||
type="text"
|
||||
x-model="searchQuery"
|
||||
@input="searchNotes()"
|
||||
@input="debouncedSearchNotes()"
|
||||
:placeholder="t('sidebar.search_placeholder')"
|
||||
class="w-full px-2 py-1.5 pl-7 pr-7 text-xs rounded focus:outline-none focus:ring-1"
|
||||
style="background-color: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border-primary);"
|
||||
|
|
@ -1680,7 +1680,12 @@
|
|||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="searchQuery.trim() && searchResults.length === 0">
|
||||
<template x-if="searchQuery.trim() && isSearching">
|
||||
<div class="p-6 text-center">
|
||||
<p class="text-sm" style="color: var(--text-tertiary);" x-text="t('search.searching', {query: searchQuery})"></p>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="searchQuery.trim() && !isSearching && searchResults.length === 0">
|
||||
<div class="p-6 text-center">
|
||||
<svg class="w-12 h-12 mx-auto mb-3 opacity-40" style="color: var(--text-tertiary);" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@
|
|||
"match_of": "Treffer {{current}} von {{total}}",
|
||||
"result_singular": "Ergebnis",
|
||||
"result_plural": "Ergebnisse",
|
||||
"no_results": "Keine Notizen enthalten \"{{query}}\""
|
||||
"no_results": "Keine Notizen enthalten \"{{query}}\"",
|
||||
"searching": "Suche nach \"{{query}}\"..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@
|
|||
"match_of": "Match {{current}} of {{total}}",
|
||||
"result_singular": "result",
|
||||
"result_plural": "results",
|
||||
"no_results": "No notes contain \"{{query}}\""
|
||||
"no_results": "No notes contain \"{{query}}\"",
|
||||
"searching": "Searching for \"{{query}}\"..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@
|
|||
"match_of": "Match {{current}} of {{total}}",
|
||||
"result_singular": "result",
|
||||
"result_plural": "results",
|
||||
"no_results": "No notes contain \"{{query}}\""
|
||||
"no_results": "No notes contain \"{{query}}\"",
|
||||
"searching": "Searching for \"{{query}}\"..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@
|
|||
"match_of": "Coincidencia {{current}} de {{total}}",
|
||||
"result_singular": "resultado",
|
||||
"result_plural": "resultados",
|
||||
"no_results": "Ninguna nota contiene \"{{query}}\""
|
||||
"no_results": "Ninguna nota contiene \"{{query}}\"",
|
||||
"searching": "Buscando \"{{query}}\"..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@
|
|||
"match_of": "Résultat {{current}} sur {{total}}",
|
||||
"result_singular": "résultat",
|
||||
"result_plural": "résultats",
|
||||
"no_results": "Aucune note ne contient \"{{query}}\""
|
||||
"no_results": "Aucune note ne contient \"{{query}}\"",
|
||||
"searching": "Recherche de \"{{query}}\"..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@
|
|||
"match_of": "Megfelelő {{current}} / {{total}}",
|
||||
"result_singular": "eredmény",
|
||||
"result_plural": "eredmények",
|
||||
"no_results": "Nincs jegyzet amely tartalmazza a következőt: \"{{query}}\""
|
||||
"no_results": "Nincs jegyzet amely tartalmazza a következőt: \"{{query}}\"",
|
||||
"searching": "\"{{query}}\" keresése..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@
|
|||
"match_of": "Risultato {{current}} di {{total}}",
|
||||
"result_singular": "risultato",
|
||||
"result_plural": "risultati",
|
||||
"no_results": "Nessuna nota contiene \"{{query}}\""
|
||||
"no_results": "Nessuna nota contiene \"{{query}}\"",
|
||||
"searching": "Ricerca di \"{{query}}\"..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@
|
|||
"match_of": "{{total}}件中{{current}}件目",
|
||||
"result_singular": "件",
|
||||
"result_plural": "件",
|
||||
"no_results": "「{{query}}」を含むノートはありません"
|
||||
"no_results": "「{{query}}」を含むノートはありません",
|
||||
"searching": "「{{query}}」を検索中..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@
|
|||
"match_of": "Результат {{current}} из {{total}}",
|
||||
"result_singular": "результат",
|
||||
"result_plural": "результатов",
|
||||
"no_results": "Нет заметок с \"{{query}}\""
|
||||
"no_results": "Нет заметок с \"{{query}}\"",
|
||||
"searching": "Поиск \"{{query}}\"..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@
|
|||
"match_of": "Zadetek {{current}} od {{total}}",
|
||||
"result_singular": "rezultat",
|
||||
"result_plural": "rezultatov",
|
||||
"no_results": "Nobena beležka ne vsebuje \"{{query}}\""
|
||||
"no_results": "Nobena beležka ne vsebuje \"{{query}}\"",
|
||||
"searching": "Iskanje \"{{query}}\"..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@
|
|||
"match_of": "第 {{current}} 个,共 {{total}} 个",
|
||||
"result_singular": "个结果",
|
||||
"result_plural": "个结果",
|
||||
"no_results": "没有笔记包含\"{{query}}\""
|
||||
"no_results": "没有笔记包含\"{{query}}\"",
|
||||
"searching": "正在搜索\"{{query}}\"..."
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue