Unified global search & command palette (closes #16) - #31
Merged
Conversation
Issue #16 asked for a Spotlight over the whole library. Two things were in the way: search could not see the half of the library the LLM wrote, and the box it was typed into could search but not act. Summaries, action items and translations all live in analysis.result_text, which nothing indexed — so the words a user is most likely to remember reading were the words search could not find. A new FTS5 index covers them, and core/search.py answers a query from five places at once: transcripts, LLM output, names and aliases, tags, folders. A recording that matches in several is still one result, carrying the strongest match as its snippet and naming every place it was found. The route moves out of folders_tags.py, which was never where the app's central hub belonged. The header search box is now a command palette on Ctrl/Cmd+K, reachable mid-sentence from inside any text field. It searches as you type and runs any command in the app; / @ # narrow it to commands, recordings, or folders and tags — the prefixes the TUI palette already uses, with its fuzzy matcher ported so both rank the same way. Three smaller things the work turned up: - A recording renamed with an alias could not be found by that name. Search read the file name only — the one name the user had chosen was the one it ignored. - The old dropdown put snippets straight into innerHTML. They carry <mark> around the match and are otherwise raw transcript text, so a transcript discussing a script tag was one. - offset was applied per source and then merged, so paging skipped rows and repeated others. It now walks the merged list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017gRFov5dsnRfubA6Rv8cX4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #16.
The issue asked for a Spotlight over the whole library. Two things were in the way: search could not see the half of the library the LLM wrote, and the box it was typed into could search but not act.
Search now reads everything
Summaries, action items and translations all live in
analysis.result_text, which nothing indexed — so the words a user is most likely to remember reading were the words search could not find. Migration 9 adds a trigger-maintained FTS5 index over it, and a newbackend/core/search.pyanswers a query from five places at once:kindtranscripttranscript.full_textsummaryanalysis.result_texttitletagfolderA recording that matches in several places is still one result: it carries its strongest match as the snippet and reports every place in
matched_in. Ranking is by kind in that order — a better place always outranks a worse one, and matching in several lifts a result only within its own band. The route moved out offolders_tags.pyintoapi/routes/search.py; the response keeps its old keys, so the TUI is unaffected.The header box became a command palette
Ctrl/⌘+ K opens it from anywhere, including from inside a text field. It searches as you type and runs any command in the app. A leading character narrows it, using the prefixes the TUI palette already uses:/@#Commands, folders and tags are matched in the browser and appear instantly; recordings come from
/api/search, debounced.tui/fuzzy.py's scorer is ported to JS so both palettes rank the same way.Three defects the work turned up
innerHTMLunescaped. They carry<mark>around the match and are otherwise raw transcript text, so a transcript discussing a script tag was one. The palette escapes the snippet whole and restores only those two tags.offsetwas applied per source and then merged, so paging skipped rows and repeated others. It now walks the merged list.Behaviour change
Typing in the header no longer live-filters the library list as you type. That now happens on the palette's last row ("Show every result for … in the library") or on Enter. The live filter and the results dropdown were two overlapping UIs on one box, and only one of them could be the hub.
Verification
ruff --select Fclean.tests/test_search_unified.py(13 — the five sources, one-row-per-recording, ranking between kinds, offset paging, trigger upkeep when an analysis is filled in later or deleted) andtests/test_frontend_command_palette.py(6 — every element id the palette reaches for exists in the markup, the documented prefixes are the implemented ones, the escape step is still there).Two rough edges the browser pass caught and fixed before this landed: fuzzy command matches were burying library hits ("anna" is a subsequence of "Ask your library"), so mixed-mode commands now need a substring match; and ranked commands repeated their section headers, so rows are grouped by section before rendering.
Docs updated:
docs/doc.md(new API section),README.md,docs/ROADMAP.md,CHANGELOG.md.