feat: full-text thread search#8288
Conversation
…cator - Lazy-build full-text search corpus of message content when search dialog opens - Incremental invalidation on message add/update/delete for near real-time updates - Loading spinner shown while building index to keep user informed - Strict substring matching for title and content search (no fuzzy false positives) - Debounced search input for smooth UI performance
PR Review: feat: full-text thread searchSummaryThis PR adds full-text search across message content in the thread search dialog. Previously, search was title-only (via fuzzy matching with
4 files changed, +358 / -17 lines, 1 commit. Detailed Findings1. Correctness Issues
The The Suggested fix: Fallback logic can show stale/duplicate results In if (fullTextResults.length > 0) {
filteredThreads = fullTextResults.map((r) => r.thread)
} else {
filteredThreads = getFilteredThreads(searchQuery)
}The fallback uses Suggestion: Use 2. Concurrency / Race Condition
If This is mitigated by the fact that 3. Memory / PerformanceNo upper bound on corpus size Each thread's content is capped at 5,000 chars, which is good. But there's no limit on the total number of threads indexed. A power user with 1,000+ threads would have the entire corpus in memory. For most users this is fine, but it would be worth documenting the scaling characteristics or adding a thread count cap.
The method 4. UX ConcernsSnippet only shown for content-only matches In the search method: snippet: contentMatch && !titleMatch
? extractSnippet(entry.contentText, term)
: undefinedWhen "No results found" may flash during index build If the index is still building ( 5. Code Quality
6. TestingNo tests are included. The Recommendation: fix neededThe Nice feature overall -- the architecture (singleton index, lazy build, incremental invalidation) is well thought out and the code is clean. |
- hasPendingWork is now a method taking threads so newly created threads are detected and indexed without requiring a full invalidation - build() loops until no pending work remains, so invalidations that arrive mid-build are not silently dropped (race condition fix) - fallback-to-fzf now gates on indexReady rather than result count, preventing fuzzy false positives once the index is built - snippet is shown for all content matches including matchSource 'both' - 'Still indexing…' empty state prevents 'No results' flash during build - removed dead rebuildIndex() no-op - MAX_INDEXED_THREADS=2000 cap with documented scaling characteristics - extractTextFromContent now uses proper ThreadContent[] type (no `any`) - add 22 unit tests for search-index module Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes applied
Fallback-to-fuzzy inconsistency Mid-build race condition No upper bound on corpus size
Snippet for all content matches "No results" flash during build
Misleading "fuzzy matching" comment Tests addedAdded
|
Describe Your Changes
Previously, the search bar only searched through chat titles. This meant if you remembered discussing something in a conversation but didn't remember the title, you couldn't find it. Now the search indexes all message content across all your chats, so searching for a topic like "python async" will find any thread where that topic was discussed even if the title is something generic like "Code Help". More specifically:
Self Checklist