diff --git a/frontend/app.js b/frontend/app.js
index e210f63..6026c5d 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -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();
diff --git a/frontend/index.html b/frontend/index.html
index 1b7c1b6..a4e0131 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1621,7 +1621,7 @@
-
+
+
+
+