Fix search not working
This commit is contained in:
parent
f229bb978e
commit
a6589fb1f3
@ -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
34
dist/index.html
vendored
@ -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);
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar {sidebarOpen ? 'open' : ''}">
|
||||
<Sidebar on:back={handleBack} on:goList={goList} />
|
||||
<Sidebar />
|
||||
</aside>
|
||||
|
||||
<!-- Main content -->
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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'
|
||||
}
|
||||
|
||||
|
||||
@ -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('')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user