Add search in five minutes or press ⌘K to watch it search these docs.

API

SearchOverlay component

SearchOverlay.astro renders the ⌘K command palette. Add it once in a global layout. It’s a <dialog> that opens over the page; it doesn’t affect layout until opened.

---
import SearchOverlay from "@hev/ask/components/SearchOverlay.astro";
---
<SearchOverlay />

Props

PropTypeDefaultDescription
endpointstring'/api/ask'Endpoint the overlay posts queries to. Must match the integration’s endpoint option.
placeholderstring'Search the docs…'Placeholder text for the input.
debouncenumber500Milliseconds after typing stops before a keyword query is sent.
<SearchOverlay
  endpoint="/api/ask"
  placeholder="Search hev ask…"
  debounce={400}
/>

Opening the overlay

Two ways, both built in:

  • ⌘K / Ctrl-K is bound automatically once the component is on the page.
  • Any element with data-hev-ask-open opens it on click — wire up as many triggers as you like (header button, sidebar, inline links).
<button type="button" data-hev-ask-open>
  Search <kbd>⌘K</kbd>
</button>

<a href="#" data-hev-ask-open>Search the docs</a>

Keyboard model

The overlay is ask-first: the number of words you’ve typed decides the path.

  • Open (with AI on) → a few suggested questions appear. Click one — or press Tab to drop the first into the input — to ask it.
  • Type one word → debounced keyword search runs; the first result is auto-active. This is the instant, keyless lookup path.
  • Type a space (a second word) → keyword type-ahead stops and the overlay switches to ask mode: the results area shows an Ask AI affordance and Enter sends the question to the agentic loop.
  • Enter → in ask mode, runs the agentic loop. In keyword mode, opens the active result. (If you moved the selection with / on a single-word query, Enter opens that result instead of asking.)
  • / → move the active keyword result. Esc → close.

So type a question → Enter asks AI, and type one word → Enter (or ↓↓ → Enter) opens a keyword hit. The footer hint reflects the current mode.

Suggested questions

When AI is on, the overlay fetches a short list of suggested questions from the endpoint (GET /api/ask) the first time it opens and shows them in the empty state. They come from the knowledge graph’s suggestions, baked in at build time, so there’s no model call to render them — and if the graph has none, the overlay simply shows nothing extra. Clicking a suggestion fills the input and asks it immediately.

The mode toggle

The overlay persists an “AI on Enter” preference in localStorage under the key hev-ask:mode (agentic or keyword). Readers who flip it to keyword-only never trigger a model call: a space just searches for a phrase, and no suggested questions are shown. The choice survives reloads.

Each keyword result row renders the document title, an optional heading breadcrumb (Concepts › The agentic loop), and a one-line snippet. The row’s link is the chunk’s url, which already carries the #anchor — so clicking a result lands on the exact heading.

The streamed answer

Pressing Enter (with a key configured) replaces the keyword rows with an answer panel. The sub-queries the model runs appear live as a faint searched: … line, then the grounded answer streams in token-by-token with a blinking caret until it completes. Inline deep links in the prose are styled in the accent color and point at the exact /docs/page#anchor; hovering shows the section breadcrumb. A Sources chip row beneath the answer lists every section it drew from.

Links are validated against the source set the endpoint streamed: any link the model emits to a URL outside that set is rendered as plain text, so a hallucinated anchor can never become a clickable dead link.

Theming

The overlay’s markup uses the as- class prefix and reads your page’s CSS custom properties. Define these on :root (this site’s values shown) and the overlay inherits your palette:

:root {
  --paper: #111111;       /* overlay background */
  --ink: #fafaf5;         /* primary text */
  --muted: #6b6b66;       /* secondary text */
  --signal: #e25822;      /* accent / active state */
}

Because the component ships scoped styles keyed to these variables, matching your site’s look is usually just defining the tokens — no overlay CSS to override.

esc