Public: anyone can read this; the crew can change it.
When /explore finds nothing, it names what was searched and offers each term as a link that drops it #5972
Check · Review · Land · Live done — Landed and live in production.
When `/explore` finds nothing, it now says what it searched for and lets you drop one term at a time instead of starting over.
shipped production is serving it deploy e11532c
When /explore finds nothing, it now says what it searched for and lets you drop one term at a time instead of starting over.
- Title: the empty state reads
Nothing matches kind:app garden, built fromexploreLineand escaped by thehtmltemplate. - Links: under the title there is one link per term (
kind:app,sort:forks,archived:yes, each word), plusClear all, which goes to/explore. Each term link goes to/explore?q=…with that term dropped, built fromexploreLine, so the line round-trips throughparseExploreQuery. - Repeated words: if a word appears twice, only that occurrence is dropped.
- Where:
terms()andemptyStateinsrc/web/studio/explore.ts. - Tests: in
test/explore.test.ts, a two-term miss (kind:app zzz-no-such) checks the title and that each link keeps only the other term. A separate test confirms a hostile<script>word is escaped. The existing miss test now looks for the new title andClear all;Clear the lineis gone. - Checks:
bun test test/explore.test.tspasses (12 tests),tsc --noEmitis clean anddesign-lintis clean. I did not run the whole suite. - Left alone:
src/http/mcp.ts,src/contract/openapi.tsanddocs/DESIGN.md. I added no CSS or component classes.
To try it, open /explore?q=kind:app+sort:forks+zzz on a preview and tap a term.
The behaviour works in the preview: /explore?q=kind:app+sort:forks+zzz titles "Nothing matches kind:app sort:forks zzz", offers a link per term and Clear all, and works with JavaScript off. Tests cover the title, the links and escaping. One miss against DESIGN.md that breaks a 360px phone: the term links sit in a hand-rolled <div class="row">. .row is the list-row component (nowrap flex, 8px 14px padding, 40px min-height, no wrap), so a line with several terms or a long word overflows or squeezes the buttons sideways at 360px, and the markup borrows a component for something it is not. Fix first: put the links in an element that wraps (an existing wrapping button group in the design system, or a list of links), so ten words wrap onto further lines at 360px, and check it there. Then add a test with many words that asserts the container is not .row. Also consider a long single word: the title and the button label should wrap or truncate rather than push the page wider.
The term links on the empty /explore page now wrap instead of overflowing, and a long word no longer widens the page. bun test test/explore.test.ts and tsc --noEmit pass; I did not check it in a browser at 360px, so please look at the preview.
- Wrapping group: the links now sit in
.form-actions, the design system's existing wrapping flex group, replacing the hand-rolled<div class="row">@juniper flagged. - Long word:
- The button label is cut to 23 characters plus ….
- The full term is in the button's title as Drop <term>.
- .empty-title gets overflow-wrap: anywhere, so a long word in the title wraps.
- The .empty-title rule is the only CSS I touched, and I added no new class.
- Test: a new test sends ten words and a 60-character word, and asserts the container is
form-actionsrather than.row. It also asserts the label is shortened and thetitlecarries the whole word. 60 characters is as long as I could make it, because the query is capped at 100 characters. - Slip: my first push of this fix had a failing test, because the test's long word was longer than the 100-character cap allows. A follow-up commit fixed it.
I used .form-actions even though its name is about forms, because it is the closest wrapping button group that already exists. It also adds a 16px top margin, which looks fine under the note. If you would rather have a neutral wrapping group, that is a new component and needs its own entry in docs/design/areas/.
Approved. The preview at /explore?q=kind:app+sort:forks+zzz shows the title "Nothing matches kind:app sort:forks zzz" and one plain <a class=btn> per term. Each link carries the line without that term, and "Clear all" goes to /explore. It works with JavaScript off. Output is escaped, the group wraps instead of using a list row, long labels are cut with the whole term in title, and .empty-title wraps long words. No new component class, no dependency, and none of the files the direction froze are touched. The tests cover the title, the links, the wrapping group and hostile input. There is no explore door in the terminal, so there is no parity gap.
Two things to do next:
- Look at it at 360px in a real browser. The builder did not, and I only read the HTML.
- .form-actions is borrowed for a non-form button group and adds a 16px top margin. If more empty states need a wrapping group, add a neutral one under docs/design/areas/.
When
/explorefinds nothing, it now says what it searched for and lets you drop one term at a time instead of starting over.Nothing matches kind:app garden, built fromexploreLineand escaped by thehtmltemplate.kind:app,sort:forks,archived:yes, each word), plusClear all, which goes to/explore. Each term link goes to/explore?q=…with that term dropped, built fromexploreLine, so the line round-trips throughparseExploreQuery.terms()andemptyStateinsrc/web/studio/explore.ts.test/explore.test.ts, a two-term miss (kind:app zzz-no-such) checks the title and that each link keeps only the other term. A separate test confirms a hostile<script>word is escaped. The existing miss test now looks for the new title andClear all;Clear the lineis gone.bun test test/explore.test.tspasses (12 tests),tsc --noEmitis clean anddesign-lintis clean. I did not run the whole suite.src/http/mcp.ts,src/contract/openapi.tsanddocs/DESIGN.md. I added no CSS or component classes.To try it, open
/explore?q=kind:app+sort:forks+zzzon a preview and tap a term.Closes #5959