Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions frontend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -223,6 +224,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'}, ...]
Expand Down Expand Up @@ -1418,6 +1423,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();
Expand All @@ -1427,6 +1433,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)
);
Expand All @@ -1437,6 +1444,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();
Expand All @@ -1461,6 +1469,9 @@ function noteApp() {
}
} catch (error) {
console.error('Search failed:', error);
this.searchResults = [];
} finally {
this.isSearching = false;
}
}
},
Expand Down Expand Up @@ -3831,6 +3842,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();
Expand Down
9 changes: 7 additions & 2 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1617,7 +1617,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);"
Expand Down Expand Up @@ -1676,7 +1676,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>
Expand Down
3 changes: 2 additions & 1 deletion locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 2 additions & 1 deletion locales/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 2 additions & 1 deletion locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 2 additions & 1 deletion locales/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 2 additions & 1 deletion locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 2 additions & 1 deletion locales/hu-HU.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 2 additions & 1 deletion locales/it-IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 2 additions & 1 deletion locales/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@
"match_of": "{{total}}件中{{current}}件目",
"result_singular": "件",
"result_plural": "件",
"no_results": "「{{query}}」を含むノートはありません"
"no_results": "「{{query}}」を含むノートはありません",
"searching": "「{{query}}」を検索中..."
},

"theme": {
Expand Down
3 changes: 2 additions & 1 deletion locales/ru-RU.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@
"match_of": "Результат {{current}} из {{total}}",
"result_singular": "результат",
"result_plural": "результатов",
"no_results": "Нет заметок с \"{{query}}\""
"no_results": "Нет заметок с \"{{query}}\"",
"searching": "Поиск \"{{query}}\"..."
},

"theme": {
Expand Down
3 changes: 2 additions & 1 deletion locales/sl-SI.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 2 additions & 1 deletion locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@
"match_of": "第 {{current}} 个,共 {{total}} 个",
"result_singular": "个结果",
"result_plural": "个结果",
"no_results": "没有笔记包含\"{{query}}\""
"no_results": "没有笔记包含\"{{query}}\"",
"searching": "正在搜索\"{{query}}\"..."
},

"theme": {
Expand Down