pocket-es Search UX Spec: Dual-Input Redesign
Table of Contents
1. Context
The input layer is broken in both directions. The placeholder advertises a raw ES payload, but typing plain text produces no results, and typing valid JSON also fails silently. The single box conflates two incompatible interaction models. This spec replaces it with two explicit modes.
2. Goals
- Common case (casual visitor typing a word) works by default
- Full ES DSL power for advanced users behind an explicit toggle
- Both paths feed a single shared result renderer
- No changes to the engine API (
window.pocketES)
3. Component Structure
Two mutually exclusive input modes plus shared output:
3.1. Simple mode (default)
Single-line <input class"pes-input–simple">= for plain-text queries.
Live autocomplete wired to .pes-suggestions.
3.2. Advanced mode
Multi-line <textarea class"pes-input–advanced">= for hand-written ES
DSL payloads. Revealed by toggle (e.g. "Advanced: raw ES query").
3.3. Shared
Single #pes-results panel and one render function consumed by both modes.
4. Behavior
4.1. Simple mode
On input (debounced ~150ms):
{:query {:multi_match {:query <text>
:fields ["title^3" "keywords^2" "description" "headings"]}}}
- Empty input clears results to neutral placeholder
.pes-suggestionspopulated frompocketES.suggest({prefix: <text>})- Selecting a suggestion fills input and runs search
- Suggestions cap at ~8 items
4.2. Advanced mode
- Textarea contents parsed as JSON, passed to
pocketES.search(...) - Debounced ~300ms or explicit "Run" action
- Placeholder shows representative payload
- "Copy as console call" affordance (nice-to-have)
4.3. Placeholder copy
- Simple:
"Search the site..." - Advanced: ES DSL template
- Current ES-DSL placeholder on a plain search box must not survive
5. Result Rendering
Single render function, three states:
| State | Behavior |
|---|---|
| Hits | Title, path link, visible BM25 _score, ordered descending |
| Empty | "No matches for <query>" — not the idle placeholder |
| Idle | "Type to search..." when input is empty |
6. Error Handling (Advanced Mode)
Silent failure is eliminated:
- Invalid JSON: inline message
Invalid JSON: <reason> - Malformed query:
Could not run query: <reason> - Users must distinguish "no results" from "query didn't execute"
7. Acceptance Criteria
- Default to simple mode with plain-language placeholder
- Typing
clojurerenders BM25-ordered hits - Prefix typing populates suggestions; selecting one runs search
- Zero-match query shows explicit empty-state message
- Advanced mode toggle reveals textarea; valid payloads render results
- Invalid JSON or unsupported clause renders visible error
- Each hit shows or is ordered by BM25 score
- No changes to
window.pocketESengine API
8. Out of Scope
Engine/algorithm changes, index build pipeline, data-source loading paths. Confined to ClojureScript input rendering and result-display layer.
9. Status
Tracked as www.wal.sh-nf1s. Blocked on stable Clojure e2e in prod.