KQ-2115 · FALLTHROUGH/ASK

Fallthrough Ask

A command palette that never dead-ends: typing filters commands and pages with fuzzy matching under a highlight pill that slides on springs.snap, every query ends with an ask row, and a query that matches nothing turns the empty row into it while pages sharing a word stay listed as Related. Enter unfolds the ask row in place on springs.glide into an answer card — while it reads, the pages it is reading arrive one by one on snap; the answer then rises sentence by sentence with citation marks popping in on springs.flick, and its sources settle as chips, a numbered list or footnotes. The field is a combobox whose Up and Down move one highlight across results and the answer (the ask option is described by the answer text), Tab walks the sources, Escape folds the answer and keeps the query, and the no-answer and error states keep it too while still offering a next step.

KQ-2115 · FALLTHROUGH/ASK

Up and Down move through the results and the ask row, Enter opens or asks, Tab reaches an answer’s sources, Escape folds the answer.

Nothing matches “how many unused days carry over”

Ask the handbook“how many unused days carry over”↵ Ask
Leave and holidays
Holiday calendar 2026
Expenses and travel
esc clears

asks when nothing matches · “how many unused days carry over”

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/fallthrough-ask

Props

ref
React.Ref<FallthroughAskHandle>
default —
ask(query?) asks the assistant (the field's query, or this one), fold() folds the answer, focus() puts focus in the field.
commands
AskCommand[]
default defaultAskCommands
Commands the palette runs: { id, label, icon, shortcut?, keywords? }.
pages
AskPage[]
default defaultAskPages
Pages it opens and answers cite: { id, title, path, excerpt?, keywords? }; excerpts and keywords also find related pages.
answers
AskAnswer[]
default defaultAskAnswers
Answers it can give without a host: { id, keywords, text with [1]-style marks, sources (page ids in mark order) }.
recent
string[]
default ["leave", "expenses", "first-week"]
Page ids listed for an empty query.
query / defaultQuery
string
default ""
The field's text, controlled or initial.
onQueryChange
(query: string) => void
default —
Fires on every keystroke with the new query.
onSelect
(item: { kind: "command" | "page"; id: string }) => void
default —
A command or page was chosen with Enter or a click.
onAsk
(query: string) => Promise<AskAnswer | null> | void
default —
The visitor asked. Return a promise of an answer (null for none) to answer yourself — a rejection shows the error state; return nothing and the palette answers from answers after thinkingMs.
onOpenSource
(pageId: string, answerId: string | null) => void
default —
A source page was opened from an answer (answerId is null for the no-answer state's closest page).
onAnswer
(state: "answered" | "none" | "error", answer: AskAnswer | null) => void
default —
The assistant settled.
thinkingMs
number
default 1200
How long the palette reads before answering from answers, in ms.
answerHeight
number
default 200
The tallest the answer grows before it scrolls inside the card, in px.
sourceStyle
"chips" | "list" | "notes"
default "chips"
How sources show: numbered page chips, a list with each page's path, or footnote marks with a source line.
accent
"cobalt" | "plum" | "mint" | "ink" | string
default "cobalt"
Highlight, ask disc and citations. A name or any CSS colour.
assistantName
string
default "Handbook assistant"
Who answers, in the card header and the footer.
placeholder
string
default "Search or ask the handbook"
The field's placeholder.
maxHeight
number
default 460
The palette's tallest height in px; its body follows its content on glide up to this.
label
string
default "Search the Fernworks handbook"
The field's accessible name.
disabled
boolean
default false
Read only: nothing can be typed, chosen or asked.
className
string
default —
Classes for the palette.