perf(unified-search): skip redundant loading-spinner DOM rebuild on every keystroke
Summary
The unified-search input handler calls showUnifiedSearchLoading('Searching...') on every input event, and that function unconditionally rebuilds the spinner DOM (replaceChildren + 3 createElement + 2 style writes) even when the spinner is already displayed.
| Field | Value |
|---|---|
| Severity | suggested |
| Origin | Review of #5440 (post-merge extraction) |
| Review section | Suggested |
| Classification | PR-introduced (per-keystroke call added in #5440; rebuild itself pre-existing) |
| Verified at | main@99c07fe97fca |
| Dedup | distinct · 0 hits reviewed |
Trigger
Type multiple characters into the library search bar: each keystroke enters the input handler, which (for queries at/above MIN_QUERY_LENGTH) calls showUnifiedSearchLoading before arming the debounce timer. Every call discards and recreates the spinner nodes even though the spinner from the previous keystroke is still on screen.
Impact
Wasteful per-keystroke DOM churn during typing (write-only, no layout thrash today) and results→spinner→spinner transitions mid-typing. Degrades on slow devices and long fast-typing bursts; no correctness break.
Pinned evidence
99c07fe97fca:src/local_deep_research/web/static/js/pages/unified_search.js:L149— unconditional per-keystrokeshowUnifiedSearchLoading('Searching...')in the input handler99c07fe97fca:src/local_deep_research/web/static/js/pages/unified_search.js:L226-L240—showUnifiedSearchLoadingalways doesreplaceChildren()+ node creation + style writes, with no already-loading short-circuit
Fix direction
Track a lightweight UI state ('idle' | 'loading' | 'results', e.g. alongside unifiedSearchRunId) and short-circuit showUnifiedSearchLoading when the spinner is already displayed; reset the state on idle/results/error renders.
Targeted tests
Add a vitest case: dispatch two consecutive input events (query ≥ MIN_QUERY_LENGTH), capture the spinner node after the first keystroke, and assert the same node instance survives the second (node identity — childElementCount cannot detect replacement with an equivalent spinner); alternatively spy on document.createElement/replaceChildren and assert a single rebuild. Then assert results still render after debounce.
Provenance
Extracted from review of PR #5440 (post-merge extraction).
Source: LearningCircuit/local-deep-research