Fix search not working

This commit is contained in:
Timothy Farrell 2026-05-15 23:52:39 +00:00
parent f229bb978e
commit a6589fb1f3
7 changed files with 60 additions and 19 deletions

View File

@ -31,7 +31,7 @@ src/
## Key Design Decisions
- **Svelte 5 runes** — Use `$state`, `$derived`, `$effect`. Props-based event passing (no Svelte events).
- **Svelte 5 runes** — Use `$state`, `$derived`, `$effect`. Props-based event passing (no Svelte events). Event handler attributes are all lower-case (e.g. oninput)
- **No external crypto libraries** — Uses the browser's native Web Crypto API exclusively.
- **Key never persisted** — Encryption key lives only in `$state` memory; cleared on lock, tab switch, or page close.
- **Single-file build**`vite-plugin-singlefile` inlines all JS/CSS; post-build script inlines favicon.

34
dist/index.html vendored
View File

@ -5763,6 +5763,7 @@ function LockScreen($$anchor, $$props) {
}
//#endregion
//#region src/lib/stores/search.svelte.js
var DEBOUNCE_MS = 300;
var SearchStore = class {
#query = /* @__PURE__ */ state("");
get query() {
@ -5771,6 +5772,13 @@ var SearchStore = class {
set query(value) {
set(this.#query, value, true);
}
#debouncedQuery = /* @__PURE__ */ state("");
get debouncedQuery() {
return get(this.#debouncedQuery);
}
set debouncedQuery(value) {
set(this.#debouncedQuery, value, true);
}
#activeGroupId = /* @__PURE__ */ state("all");
get activeGroupId() {
return get(this.#activeGroupId);
@ -5785,8 +5793,21 @@ var SearchStore = class {
set refreshTrigger(value) {
set(this.#refreshTrigger, value, true);
}
#debounceTimer = null;
setSearchQuery(value) {
this.query = value;
if (this.#debounceTimer) clearTimeout(this.#debounceTimer);
if (value === "") {
this.debouncedQuery = "";
this.#debounceTimer = null;
} else this.#debounceTimer = setTimeout(() => {
this.debouncedQuery = value;
this.#debounceTimer = null;
}, DEBOUNCE_MS);
}
clear() {
this.query = "";
this.debouncedQuery = "";
this.activeGroupId = "all";
}
/** Force subscribed components to re-fetch data. */
@ -6007,13 +6028,13 @@ function Sidebar($$anchor, $$props) {
set_value(input, search.query);
set_class(button, 1, `group-item ${search.activeGroupId === "all" ? "active" : ""}`, "svelte-181dlmc");
});
event("Input", input, (e) => search.query = e.target.value);
delegated("input", input, (e) => search.setSearchQuery(e.target.value));
delegated("click", button, () => search.activeGroupId = "all");
delegated("click", button_4, () => openGroupForm(null));
append($$anchor, div);
pop();
}
delegate(["click"]);
delegate(["input", "click"]);
//#endregion
//#region src/components/EntryList.svelte
var root_1$6 = /* @__PURE__ */ from_html(`<div class="loading svelte-13s7gu4">Loading entries...</div>`);
@ -6034,7 +6055,7 @@ function EntryList($$anchor, $$props) {
set(loading, true);
set(error, "");
try {
const query = search.query.trim();
const query = search.debouncedQuery.trim();
const groupId = search.activeGroupId;
if (query) set(entries, await searchEntries(query, groupId !== "all" ? { groupId } : {}), true);
else if (groupId !== "all") set(entries, await getEntries({ groupId }), true);
@ -6046,7 +6067,7 @@ function EntryList($$anchor, $$props) {
set(loading, false);
}
user_effect(() => {
search.query;
search.debouncedQuery;
search.activeGroupId;
search.refreshTrigger;
loadEntries();
@ -6883,10 +6904,7 @@ function MainLayout($$anchor, $$props) {
if (get(sidebarOpen)) $$render(consequent);
});
var aside = sibling(node, 2);
Sidebar(child(aside), { $$events: {
back: handleBack,
goList
} });
Sidebar(child(aside), {});
reset(aside);
var main = sibling(aside, 2);
var div_2 = child(main);

View File

@ -13,7 +13,7 @@
loading = true
error = ''
try {
const query = searchStore.query.trim()
const query = searchStore.debouncedQuery.trim()
const groupId = searchStore.activeGroupId
if (query) {
@ -35,9 +35,9 @@
loading = false
}
// Reload when search query, active group filter, or refresh trigger changes
// Reload when debounced search query, active group filter, or refresh trigger changes
$effect(() => {
searchStore.query
searchStore.debouncedQuery
searchStore.activeGroupId
searchStore.refreshTrigger
loadEntries()

View File

@ -58,7 +58,7 @@
<!-- Sidebar -->
<aside class="sidebar {sidebarOpen ? 'open' : ''}">
<Sidebar on:back={handleBack} on:goList={goList} />
<Sidebar />
</aside>
<!-- Main content -->

View File

@ -96,7 +96,7 @@
type="text"
placeholder="Search entries..."
value={searchStore.query}
onInput={(e) => searchStore.query = e.target.value}
oninput={(e) => searchStore.setSearchQuery(e.target.value)}
/>
</div>

View File

@ -3,13 +3,36 @@
* Shared between Sidebar and EntryList for coordinated filtering.
*/
const DEBOUNCE_MS = 300
export class SearchStore {
query = $state('')
query = $state('') // raw input value — bound to the search input
debouncedQuery = $state('') // debounced value — used for actual search
activeGroupId = $state('all') // 'all' or a group id
refreshTrigger = $state(0) // incremented to force a re-fetch
#debounceTimer = null
/**
* Update the search query with debouncing.
* Call this from the input handler instead of setting `query` directly.
*/
setSearchQuery(value) {
this.query = value
if (this.#debounceTimer) clearTimeout(this.#debounceTimer)
if (value === '') {
this.debouncedQuery = ''
this.#debounceTimer = null
} else {
this.#debounceTimer = setTimeout(() => {
this.debouncedQuery = value
this.#debounceTimer = null
}, DEBOUNCE_MS)
}
}
clear() {
this.query = ''
this.debouncedQuery = ''
this.activeGroupId = 'all'
}

View File

@ -18,12 +18,12 @@ describe('SearchStore', () => {
describe('query', () => {
it('should update query', () => {
search.query = 'test'
search.setSearchQuery('test')
expect(search.query).toBe('test')
})
it('should handle unicode query', () => {
search.query = 'тест'
search.setSearchQuery('тест')
expect(search.query).toBe('тест')
})
})
@ -48,7 +48,7 @@ describe('SearchStore', () => {
describe('clear()', () => {
it('should reset query to empty string', () => {
search.query = 'some search'
search.setSearchQuery('some search')
search.clear()
expect(search.query).toBe('')
})
@ -60,7 +60,7 @@ describe('SearchStore', () => {
})
it('should reset both fields simultaneously', () => {
search.query = 'some search'
search.setSearchQuery('some search')
search.activeGroupId = 'group-123'
search.clear()
expect(search.query).toBe('')