fix(search): local index rejects FTS syntax in live search queries (#983)

* fix(search): reject FTS syntax chars in local index live search queries

FTS5 treats `=` and similar characters as query syntax, so tokens like
1=2 produced unrelated prefix hits. Skip FTS for unsafe tokens instead.

* docs: CHANGELOG for local index FTS syntax query fix (PR #983)

* fix(search): reject syntax junk in server search3 and live search race

Share FTS-safe token guard with search3; skip network invoke for ** and
similar queries; show empty dropdown without misleading source badge.

* fix(search): allow censorship stars in queries, reject wildcard-only tokens

Block ** and **** but keep ***Flawless-style title searches working
in local FTS and search3.
This commit is contained in:
cucadmuh
2026-06-04 13:37:11 +03:00
committed by GitHub
parent d43a8c6691
commit 19fdba006a
10 changed files with 211 additions and 19 deletions
+4 -4
View File
@@ -5,7 +5,7 @@ import {
LIVE_SEARCH_DEBOUNCE_NETWORK_MS,
LIVE_SEARCH_DEBOUNCE_RACE_MS,
EMPTY_SEARCH_RESULTS,
liveSearchQueryTooShort,
liveSearchQueryRejected,
mergeLiveSearchResults,
runLocalLiveSearch,
runNetworkLiveSearch,
@@ -261,10 +261,10 @@ export default function LiveSearch() {
setLoading(true);
const searchT0 = performance.now();
try {
if (liveSearchQueryTooShort(q)) {
if (liveSearchQueryRejected(q)) {
if (!isStale()) {
setResults(EMPTY_SEARCH_RESULTS);
setSearchSource('local');
setSearchSource(null);
setOpen(true);
}
return;
@@ -688,7 +688,7 @@ export default function LiveSearch() {
)}
{!hasResults && !loading && (
<div className="search-empty">{t('search.noResults', { query })}</div>
<div className="search-empty">{t('search.noResults', { query: query.trim() })}</div>
)}
{share.shareMatch && (
+1 -1
View File
@@ -311,7 +311,7 @@ export default function MobileSearchOverlay({ onClose }: { onClose: () => void }
{/* ── No results ── */}
{!loading && query && !hasResults && !isLiveSearchDropdownBlocked(scope) && (
<div className="mobile-search-noresults">
{t('search.noResults', { query })}
{t('search.noResults', { query: query.trim() })}
</div>
)}