Skip to content

Feature: full-text session search with operators, accordion results, and highlighted snippets#449

Open
josegtmonteiro wants to merge 29 commits intowinfunc:mainfrom
josegtmonteiro:feat/session-search
Open

Feature: full-text session search with operators, accordion results, and highlighted snippets#449
josegtmonteiro wants to merge 29 commits intowinfunc:mainfrom
josegtmonteiro:feat/session-search

Conversation

@josegtmonteiro
Copy link

@josegtmonteiro josegtmonteiro commented Mar 13, 2026

Summary

Adds a powerful search feature to the project sessions page, enabling users to find sessions by searching through the full conversation content (all messages, not just the first one).

  • Search operators: AND (default), OR, NOT (-prefix), and "exact phrase" support
  • Accordion results: Search results display in a single-column expandable layout with matching snippets
  • Highlighted terms: Each search term is independently highlighted in snippet text
  • Action buttons: "Open session" and "Copy resume command" (claude --resume <id>) on each result row
  • Default grid preserved: When not searching, the original 3-column session card layout is unchanged

Search Syntax

Syntax Meaning Example
word1 word2 AND — both must be present altera roku
word1 OR word2 OR — at least one present outage OR downtime
-word NOT — must not be present altera -roku
"exact phrase" Literal phrase match "hold communication"
Mixed Combine freely "fix bug" refactor OR cleanup -test

Changes

Backend (Rust — src-tauri/src/commands/claude.rs)

  • Query parser (parse_query): Tokenizes input respecting quoted phrases, - negation, and OR keyword into ParsedQuery struct with and_terms, or_groups, not_terms, and highlight_terms
  • Session matching (session_matches): Reads session content once and checks AND/OR/NOT logic
  • Multi-term snippet extraction (extract_snippets_for_terms): Extracts context-windowed snippets for each positive search term
  • SessionSearchResult includes highlight_terms for frontend highlighting
  • Runs in tokio::task::spawn_blocking to avoid blocking the async runtime
  • Path traversal prevention with canonical path validation, symlink-safe scanning
  • Query validation (min 2 chars, max 256 chars), graceful error handling for unreadable files
  • 10 unit tests for the query parser
  • Web server route: GET /api/projects/{id}/sessions/search?query=...

Frontend (React/TypeScript — src/components/SessionList.tsx, src/lib/api.ts)

  • Search input with 300ms debounce, proper search-active state transitions (no flicker)
  • Accordion UI: expandable session rows with chevron toggle, match count badge
  • HighlightedText component: accepts terms: string[], highlights each independently via regex alternation
  • "Open session" button (external link icon) and "Copy resume command" button (clipboard with checkmark)
  • Accessible: aria-live region, aria-expanded/aria-controls on accordion, aria-label on all interactive elements
  • SessionSearchResult interface extended with highlight_terms: string[]

API Adapter (src/lib/apiAdapter.ts)

  • Endpoint mapping for search_project_sessions

Other files

  • src/components/TabContent.tsx — passes projectId prop to SessionList
  • src/App.tsx — passes projectId prop to SessionList

Test plan

  • cargo test — 19 tests pass (10 new parser tests + 9 existing)
  • cargo fmt -- --check — clean
  • cargo clippy — no errors
  • tsc --noEmit — clean
  • npm run check — clean
  • CodeRabbit review — 2 consecutive clean passes
  • Manual testing — built, installed to /Applications, tested with 246+ session project
  • altera roku → only sessions with BOTH terms
  • altera OR roku → sessions with either
  • altera -roku → sessions with "altera" but not "roku"
  • "hold communication" → exact phrase match
  • Expand accordion → see highlighted snippets with each term marked
  • Click open session icon → opens session without expanding
  • Click copy icon → copies claude --resume <id> to clipboard
  • Clear search → grid layout returns immediately

🤖 Generated with Claude Code

Jose Monteiro and others added 27 commits March 12, 2026 22:25
Adds a search bar to the project sessions page that searches through
all message content in session JSONL files, not just the first message.

- New Rust backend command `search_project_sessions` with case-insensitive
  full-text search across all session messages
- Web server route GET /api/projects/{id}/sessions/search?q={query}
- Frontend search UI with debounced input, loading state, and result count
- Pagination updates to reflect filtered results

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add separate error state instead of masking failures as empty results
- Use request ID ref to invalidate stale in-flight searches on
  query or project changes
- Redact search query from info-level logs (log length only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…esults

- Wrap blocking FS scan in tokio::task::spawn_blocking to avoid
  pinning async worker threads
- Validate project_id is a single normal path component to prevent
  path traversal
- Clear stale search results in the error handler

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Reject blank and oversized (>256 char) queries before scanning
- Fall back to showing all sessions on search error instead of
  clearing the list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…velope

- Add aria-label to clear search button for screen readers
- Hard reset search state on project switch to prevent stale cross-project results
- Reset page on sessions array identity change, not just length
- Make search query param optional with serde(default) to maintain
  consistent ApiResponse envelope

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion

- Add aria-label to search input for screen readers
- Clear search query and reset page on project switch
- Reset page to 1 when clearing search
- Simplify path traversal validation to work cross-platform

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…earch

- Cap search results at 100 and stop scanning early
- Use canonical path validation instead of substring matching for
  path traversal prevention
- Skip stale debounced search after project switch to avoid
  unnecessary backend I/O

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove overly strict backslash/dotdot checks, rely on canonical
  path validation for traversal prevention
- Move result cap (truncate) after sort so newest 100 matches are
  returned instead of arbitrary 100
- Use named query param in web search endpoint for adapter compat

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rity

- Use canonical path after validation to prevent TOCTOU symlink swap
- Clamp totalPages to minimum 1 for empty result sets
- Return empty success (not error) for blank web queries to match
  Tauri IPC behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Backend now returns matching text snippets with context for each
  session search result (up to 20 snippets per session)
- Search view switches to single-column accordion layout
- Each session shows match count and expands to reveal snippets
- Search terms are highlighted in snippet text
- Scrollable snippet container with max height
- Default grid layout preserved when not searching

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use semantic button element for accordion toggle with aria-expanded
  and aria-controls
- Skip symlinked session files during search scan

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use RegExp with gi flags for Unicode-safe text highlighting
- Use consistent lowered string for snippet extraction offsets
- Deduplicate session click handlers (prefer prop over event)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use char-by-char case-insensitive matching on original content
instead of slicing from lowercased string, preserving readability
in search result snippets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep search mode active while query is non-empty or search is
in-progress to prevent momentary grid layout flicker.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents sessions from disappearing during the 300ms debounce
delay before the search actually starts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Only enter search mode when query is >= 2 chars, matching the
backend minimum. Prevents 1-char input from hiding the session grid.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eader

- Open session button (external link icon) on accordion header row
- Copy resume command button copies 'claude --resume <id>' to clipboard
  with checkmark confirmation
- Removed redundant "Open session" link from expanded accordion body
- Restructured header to avoid nested buttons (a11y)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use debouncedQuery for search-active check to prevent premature
  grid disappearance
- Await clipboard write before showing success checkmark

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Log warnings and continue scanning when individual directory entries
or session files are unreadable, rather than failing the entire search.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Require both live query and debounced state for search-active mode
so clearing input immediately exits search view.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Parse query into AND terms (default), OR groups, NOT terms (-prefix),
  and "quoted phrases"
- Session matching: all AND terms present, at least one per OR group,
  no NOT terms found
- Multi-term snippet extraction with per-term context windows
- Return highlight_terms in search results for frontend highlighting
- HighlightedText component now accepts multiple terms and highlights
  each independently using regex alternation
- 10 unit tests for query parser covering all operator combinations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@josegtmonteiro josegtmonteiro changed the title Feature: full-text session search with accordion results and highlighted snippets Feature: full-text session search with operators, accordion results, and highlighted snippets Mar 13, 2026
Jose Monteiro and others added 2 commits March 13, 2026 11:25
Skip uppercase AND tokens in query parser since AND is the default
behavior. Prevents "and" from being highlighted in search results.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a search bar to the ProjectList that filters projects by name
(instant, client-side) and searches across all projects' sessions
(debounced backend call). Results are grouped by project with
expandable accordion snippets, highlighting, and action buttons.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant