From 3bf66ed125ed55bfbde95b0bc973710c2f2243b8 Mon Sep 17 00:00:00 2001 From: Caleb Allen Date: Wed, 4 Mar 2026 23:14:16 -0500 Subject: [PATCH] perf(08-01): add queueMicrotask coalescing to library store and debounce search input - Library store notify() coalesces multiple calls per microtask tick into one subscriber notification - Search input debounces store propagation by 150ms while keeping instant visual feedback - Empty search input clears immediately for responsive UX --- .../src/components/search-bar/search-bar.ts | 18 +++++++++++++++++- frontend/src/store/library-store.ts | 8 +++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/search-bar/search-bar.ts b/frontend/src/components/search-bar/search-bar.ts index 769491c..f06a5dd 100644 --- a/frontend/src/components/search-bar/search-bar.ts +++ b/frontend/src/components/search-bar/search-bar.ts @@ -10,6 +10,7 @@ import '@awesome.me/webawesome/dist/components/icon/icon.js'; @customElement('search-bar') export class SearchBar extends LitElement { private searchCtrl = new SearchController(this); + private searchDebounceTimer: ReturnType | null = null; @query('input') private inputEl!: HTMLInputElement; @@ -105,7 +106,22 @@ export class SearchBar extends LitElement { private handleInput = (e: Event) => { const input = e.target as HTMLInputElement; - this.searchCtrl.term = input.value; + const value = input.value; + + if (this.searchDebounceTimer !== null) { + clearTimeout(this.searchDebounceTimer); + this.searchDebounceTimer = null; + } + + if (value === '') { + // Instant clear for responsive feedback. + this.searchCtrl.term = ''; + } else { + this.searchDebounceTimer = setTimeout(() => { + this.searchDebounceTimer = null; + this.searchCtrl.term = value; + }, 150); + } }; private handleClear = () => { diff --git a/frontend/src/store/library-store.ts b/frontend/src/store/library-store.ts index 4f7f363..88a8c3e 100644 --- a/frontend/src/store/library-store.ts +++ b/frontend/src/store/library-store.ts @@ -46,6 +46,7 @@ class LibraryStore { }; private subscribers = new Set(); + private notifyScheduled = false; constructor() { EventsOn(Events.LibraryScanComplete, () => { @@ -340,7 +341,12 @@ class LibraryStore { } private notify(): void { - this.subscribers.forEach((callback) => callback()); + if (this.notifyScheduled) return; + this.notifyScheduled = true; + queueMicrotask(() => { + this.notifyScheduled = false; + this.subscribers.forEach((callback) => callback()); + }); } // ===================================================================