Repository navigation
feat: faceted search for the Store — topic, language, author, license - #6
Merged
Merged
Conversation
…r, license
Search the entire fledge ecosystem by what plugins actually are, not just
by name. The Store page now exposes a left filter rail backed by counts
computed from the result set, plus inline pivot from any card.
Backend
- Replace string-only `query` with a `BrowseFilters` shape (q, language,
topics[], owner, license).
- New `buildQualifiers()` composes a GitHub search string from filters,
drops reserved markers (fledge-plugin / fledge-template / fledge) so
users can't override the base topic, and quotes language values that
contain whitespace.
- New `computeFacets()` aggregates topics / languages / owners /
licenses across the result set, deduped per repo, sorted by count.
- `/api/github/browse` accepts q, language, owner, license, and
repeatable topic params; returns `{ items, total, facets, applied }`.
- `per_page` bumped from 30 to 50 for richer facets.
Frontend
- New filter rail (sticky on desktop, stacked on mobile) for Topic /
Language / Author / License, each row showing live counts and a
"Show N more" expander.
- Removable active-filter chips above the grid; "Clear all" wipes
filters in one click.
- Topic tags on cards are buttons (toggle that topic). Language and
author on each card are pivot links to filter by that value.
- Search box is debounced (300 ms), Enter forces immediate, Esc clears.
- Result count rendered top-right.
Tests
- 12 new unit tests in `tests/github.test.ts` covering qualifier
composition (empty, multi-topic, reserved-topic dropping, whitespace
quoting) and facet aggregation (sorting, per-repo dedupe, NOASSERTION
skip, empty input).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
|
👋 CorvidAgent is looking into this. |
corvid-agent
approved these changes
May 6, 2026
corvid-agent
left a comment
Collaborator
There was a problem hiding this comment.
Solid work — clean refactor with good test coverage. Approving.
What I liked:
buildQualifiers()/computeFacets()are well-separated and independently testable. The generictally()helper with per-repo dedup is a nice pattern.- Reserved topic stripping is applied consistently (both in filter construction and facet output).
- 12 tests cover the important edge cases: empty/falsy filters, whitespace quoting, NOASSERTION skip, intra-repo deduplication.
- Search debounce (300ms) with Enter bypass and Esc clear is good UX.
setSingleFiltertoggling (same value clears) gives the filter UI consistent behavior.- Responsive collapse of the filter rail at
<= 768pxis handled cleanly.
Minor observations (non-blocking):
quoteIfNeeded()wraps values with whitespace in double quotes but doesn't escape embedded quotes. Unlikely to matter with GitHub's language taxonomy, but worth a comment or guard if the filter surface expands later.- Facet counts reflect only the first 50 results (
per_page=50), not the full result set. The PR description is honest about this ("computed from the current result set"), but users might find it surprising when a filter shows count=1 for something that actually appears in many repos beyond the first page. If this becomes a problem, a follow-up could fetchtotal_countfrom the search response to indicate incomplete facets. - The
appliedfield in the API response is useful for debugging but isn't consumed by the frontend — could be dropped to keep the response lean, or documented as intentional.
Clean code, good tests, sensible UX — ship it.
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.
Summary
Make it easy to discover plugins/templates by what they actually are, not just by name.
The Store page now has a left filter rail of topics, languages, authors, and licenses with live counts computed from the current result set, plus inline pivots — click a topic tag, language stat, or author name on any card to filter by it instantly.
Backend (
src/github.ts,src/api.ts)BrowseFiltersshape (q,language,topics[],owner,license) replacing the string-only query argument.buildQualifiers()composes a GitHub search string from filters, drops the reserved base markers (fledge-plugin/fledge-template/fledge) so users can't override them, and quotes languages with whitespace.computeFacets()aggregates topics / languages / owners / licenses across the result set — deduped per repo, sorted by count,NOASSERTIONlicenses skipped./api/github/browseacceptsq,language,owner,license, and repeatabletopicquery params; responds with{ items, total, facets, applied }.per_pagebumped from 30 → 50 for richer facets.Frontend (
public/)Tests
12 new unit tests in
tests/github.test.tscovering:buildQualifiers: empty, multi-topic, reserved-topic dropping, whitespace quoting, falsy filter handling.computeFacets: sorting, per-repo dedupe,NOASSERTIONskip, empty input.bun test→ 17 pass / 0 fail.bun run typecheckclean.Test plan
bun testandbun run typecheckboth pass on CI/→ Store. Confirm filter rail loads with counts.topic:swift+language:Swift).🤖 Generated with Claude Code