Repository navigation
Description
Problem
Search accepts free text only. There is no way to narrow a query to a person,
a channel, or a date range. The from: / in: / before: / after:
operators are muscle memory for anyone coming from Slack, and a Slack-parity
gap in the sense of #2728. Today the only narrowing that exists is
programmatic: the in-channel find bar scopes by the current channel.
What already exists (the surprising part)
The entire backend path is already built. SearchQuery in
crates/buzz-search/src/query.rs carries authors, since, until, kinds
and a channel scope, and both relay search paths already map those fields
from the incoming NIP-01 filter:
- WS NIP-50 path:
crates/buzz-relay/src/handlers/req.rs(~L599) - bridge path:
crates/buzz-relay/src/api/bridge.rs(~L1698)
So a filter like {"search": "deploy", "authors": [<pubkey>], "since": …, "until": …, "#h": [<channel>]} filters correctly on the relay today. The
existing per-hit re-authorization is untouched: the relay refetches and
re-authorizes every hit, so operators cannot widen visibility ("search is
never the access boundary").
What's missing is entirely client-side: the desktop sends only
{search, search_mode, limit, "#h"} (build_search_messages_filter in
desktop/src-tauri/src/commands/messages.rs), and nothing anywhere parses
operator syntax.
Proposed v1 scope
Client-only. No new event kinds, no relay changes, no schema changes.
Syntax (parsed out of the search input; remaining text is the FTS query):
| Operator | Maps to | Notes |
|---|---|---|
from:@name |
authors |
resolved via the existing user-search / mention-candidate infra; autocomplete required, so ambiguous names are resolved at compose time |
in:#channel |
"#h" tag |
resolved against the local channel list (same source useSearchResults.ts already filters) |
after:YYYY-MM-DD |
since |
start of day, local time |
before:YYYY-MM-DD |
until |
start of day, local time |
Implementation sketch:
- Pure-TS operator parser in
desktop/src/features/search/lib/(unit-tested;
invalid operator text simply stays in the FTS query). - Autocomplete for
from:/in:in the topbar search, reusing the existing
user/channel pickers; active operators render as removable filter chips. - Extend the
search_messagesTauri command with optional
authors/since/untilparams and emit them in the filter, adding a
few lines next to the existingbuild_search_messages_filterunit tests
that pin the client/relay filter contract. - Typeahead: operators are stripped before the prefix-mode tsquery is built,
sosearch_mode: "prefix"behavior is unchanged.
Explicitly out of scope for v1 (each needs backend work or product
decisions, happy to file follow-ups):
has:link/has:image/ file-type filters (needs an indexed signal for
attachment presence)- a dedicated file/attachment search surface
- natural-language dates (
before:yesterday) - group/thread scoping operators
Open questions for maintainers
- Should operators live in the ⌘K topbar search (current mixed
channel/user/message results), or is a dedicated search view planned that
this should target instead? - Any preference on syntax? (
from:vsauthor:; whetherin:should also
accept DMs.) buzz-cli/ agents already speak raw NIP-01 filters, so they get nothing
new from this. If you'd like the same operator string parsed by
buzz-cli searchfor symmetry, that could be a follow-up.
If the approach sounds right I'm happy to implement it, splitting it as
(1) parser + command plumbing and (2) autocomplete + chips UI if you prefer
two smaller PRs.
Activity
jatinder14 commentedon Jul 25, 2026
Taking the v1 client path. Starting with the operator parser and search_messages filter plumbing (authors / since / until / channel #h), with unit tests. Autocomplete and chips can follow in a later PR if needed.
jatinder14 commentedon Jul 25, 2026
PR (parser + filter plumbing): #2871
feat(search): parse from:/in:/after:/before: and pass them in the fil…
feat(search): parse from:/in:/after:/before: and pass them in the fil…
feat(search): parse from:/in:/after:/before: and pass them in the fil…