feat(search): polish the room-header search UI/UX (LC-699) #665

Merged
longjacksonle merged 1 commit from feat/LC-699-search-polish into main 2026-08-10 20:02:23 +02:00

Polishes the room-header search popover (LC-699). Presentation + result-row enrichment only: no change to the FTS/semantic ranking, operator grammar, access gates, or the save-search mechanism. search.js combobox keyboard-nav and the role="listbox"/role="option" contract are untouched.

What changed

  • Input: leading magnifier inside the field, a clear (x) button that appears once you type and empties the field + collapses the popover, and a small "Searching..." indicator (htmx-indicator) for in-flight requests.
  • Semantic toggle: the bare checkbox is now a labelled toggle with a one-line hint ("Find by meaning, not exact words. Slower."). Its existing [data-lc-embeddings] gate already ANDs the runtime AI toggle, so it already vanishes when AI is off or embeddings are unconfigured; only the styling changed.
  • Result rows: author avatar + resolved @username byline, context label, timestamp, and the snippet with matched terms wrapped in <mark>. Names are resolved via the existing username cache (one lookup per distinct author).
  • Popover: scope header ("This room"), consistent padding, scrollable max-height list, the existing empty state, and a clearer iconned "Save search" footer.

Safety of the highlight

The snippet is HTML-escaped first, then matched on an ASCII-lowercased copy so byte offsets stay aligned with the original (ASCII lowercasing never changes byte length). Multi-byte UTF-8 is never sliced mid-character. The rendered snippet is safe pre-escaped HTML (|safe).

Tests

Two integration assertions (routes_search_room, routes_related_search) updated because the matched term is now wrapped in <mark>; they assert on the unhighlighted tail of the snippet. Full just test green (195 binaries, 1634 tests), clippy clean under both standalone and saas, cargo fmt --check clean. en/es locale parity keys added.

Scope

partials/room_header.html, search/results.html, views/search.rs (SearchResult gains author_name + author_id; snippet is safe HTML), routes/search.rs (author resolution + <mark> highlight helper), assets/main.css (authored .lc-search-* classes), locales/en+es. No DB migration, no route or access-gate changes.

🤖 Generated with Claude Code

https://claude.ai/code/session_016ufqbuN7mjyt4ms5NEygMy

Polishes the room-header search popover (LC-699). Presentation + result-row enrichment only: no change to the FTS/semantic ranking, operator grammar, access gates, or the save-search mechanism. `search.js` combobox keyboard-nav and the `role="listbox"`/`role="option"` contract are untouched. ## What changed - Input: leading magnifier inside the field, a clear (x) button that appears once you type and empties the field + collapses the popover, and a small "Searching..." indicator (htmx-indicator) for in-flight requests. - Semantic toggle: the bare checkbox is now a labelled toggle with a one-line hint ("Find by meaning, not exact words. Slower."). Its existing `[data-lc-embeddings]` gate already ANDs the runtime AI toggle, so it already vanishes when AI is off or embeddings are unconfigured; only the styling changed. - Result rows: author avatar + resolved `@username` byline, context label, timestamp, and the snippet with matched terms wrapped in `<mark>`. Names are resolved via the existing username cache (one lookup per distinct author). - Popover: scope header ("This room"), consistent padding, scrollable max-height list, the existing empty state, and a clearer iconned "Save search" footer. ## Safety of the highlight The snippet is HTML-escaped first, then matched on an ASCII-lowercased copy so byte offsets stay aligned with the original (ASCII lowercasing never changes byte length). Multi-byte UTF-8 is never sliced mid-character. The rendered snippet is safe pre-escaped HTML (`|safe`). ## Tests Two integration assertions (`routes_search_room`, `routes_related_search`) updated because the matched term is now wrapped in `<mark>`; they assert on the unhighlighted tail of the snippet. Full `just test` green (195 binaries, 1634 tests), clippy clean under both `standalone` and `saas`, `cargo fmt --check` clean. en/es locale parity keys added. ## Scope `partials/room_header.html`, `search/results.html`, `views/search.rs` (`SearchResult` gains `author_name` + `author_id`; snippet is safe HTML), `routes/search.rs` (author resolution + `<mark>` highlight helper), `assets/main.css` (authored `.lc-search-*` classes), `locales/en+es`. No DB migration, no route or access-gate changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016ufqbuN7mjyt4ms5NEygMy
feat(search): polish the room-header search UI/UX (LC-699)
All checks were successful
check-secrets / Kingfisher (push) Successful in 8s
check-secrets / TruffleHog (push) Successful in 9s
check-secrets / Nosey parker (push) Successful in 7s
check-secrets / Kingfisher (pull_request) Successful in 7s
Check / clippy + fmt + tests (pull_request) Successful in 5m24s
check-secrets / TruffleHog (pull_request) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 6s
Create release / Create release from merged PR (pull_request) Has been skipped
02857edde7
Presentation and result-row enrichment for the room-header search popover; no change to the FTS/semantic ranking, the operator grammar, the access gates, or the save-search mechanism.

Input: the leading magnifier stays inside the field and a clear (x) button appears once you type (CSS :placeholder-shown), with a small in-flight "Searching..." indicator wired via the htmx-indicator pattern (useful for semantic search, which hits the embedding model).

Semantic toggle: the bare checkbox becomes a labelled toggle with a one-line hint ("Find by meaning, not exact words. Slower.") so users understand what it does. Its existing [data-lc-embeddings] visibility gate already ANDs the runtime AI toggle (embeddings_available = ai_flag_on && state.embeddings_available() && ai_privileged), so it already vanishes when AI is off; only the styling changed.

Result rows: each hit now shows the author avatar (/avatars/{user_id}) and resolved @username byline, the room/DM context label, the timestamp, and the snippet with the matched query terms wrapped in <mark>. Author names are resolved in render_results through the existing username cache (one lookup per distinct id). Highlighting HTML-escapes the body first and matches on an ASCII-lowercased copy so byte offsets stay aligned and multi-byte UTF-8 is never sliced mid-character; the snippet is therefore safe pre-rendered HTML (|safe).

Popover: a scope header ("This room"), consistent padding, a scrollable max-height results area, the existing empty state, and a clearer, iconned "Save search" footer. New styling is authored in main.css (.lc-search-*) so it renders without a Tailwind rebuild.

Two integration assertions updated because the matched term is now wrapped in <mark> (they now assert on the unhighlighted tail of the snippet).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ufqbuN7mjyt4ms5NEygMy
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-08-10 19:58:42 +02:00
longjacksonle deleted branch feat/LC-699-search-polish 2026-08-10 20:02:23 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!665
No description provided.