AI command

Pro

A Cmd+K command palette with an AI mode. Search pages and actions, or press Tab to ask a question: the answer streams inside the palette with numbered sources, and the next steps it suggests, like filing a Linear issue or opening a Notion doc, run straight from the list.

Live · keyboard ready
  • A global Cmd+K in a SaaS app where people both jump to pages and ask questions about their workspace.
  • Search that should fall back to an AI answer when nothing matches.
  • Answers that lead straight to an action in another tool, like filing a Linear issue or posting to Slack.
  • A long, multi-turn conversation: use a chat thread.
  • Plain navigation with no AI: use command-palette.
  • Inline field suggestions while typing in a form: use an autocomplete input.

Installation

Pro source and install commands unlock with a Pro plan.

example.tsx
import { AiCommand } from "@/components/arc/ai-command/ai-command"; // Sample walkthrough<AiCommand /> // Wired to your app and model<AiCommand  items={commands}  onSelect={item => { router.push(item.id); setOpen(false); }}  onAsk={async (question, { signal, append, setSources }) => {    const res = await fetch("/api/ask", { method: "POST", body: JSON.stringify({ question }), signal });    const { sources, stream } = await readAnswer(res);    setSources(sources);    for await (const delta of stream) append(delta);    return { actions: [{ id: "issue", label: "Create issue in Linear", run: s => createIssue(question, s) }] };  }}  onClose={() => setOpen(false)}  elevated/>

API reference

3 parts. The first is the root.

AiCommand

A fixed-size command palette with an AI mode. Search pages and actions, or press Tab to ask: the answer streams in place with numbered sources and runnable next steps. With no props it plays a sample walkthrough until someone interacts.

PropTypeDefaultDescription
itemsAiCommandItem[]sample workspacePages and actions to search: { id, label, group?, icon?, meta?, shortcut?, keywords?, suggested?, onSelect? }. Rows marked suggested show while the query is empty.
onSelect(item: AiCommandItem) => void–Runs when a page or action is chosen with Enter or a click. Close your dialog or navigate here.
onAsk(question, { signal, append, setSources }) => Promise<AiCommandAnswer | void>simulated engineAnswers a question. Stream text with append, show sources early with setSources, and resolve with { text?, sources?, actions? }. Throw to show the error state with Retry. The signal aborts when the person asks again or goes back.
onAction(action: AiCommandAction, result: string) => void–Runs after a suggested action finishes, with the label it ended on (for example "Created ENG-2481").
onSourceSelect(source: AiCommandSource) => void–Runs when a source chip or citation is chosen. Sources with an href open in a new tab instead.
suggestedQuestionsstring[]3 sample questionsQuestions offered while Ask AI is open and empty.
model{ name: string; mark?: ReactNode }{ name: "Claude Sonnet 5.5" }The model that answers, shown in the footer, the input and beside each answer.
defaultMode"search" | "ask""search"Mode on first render.
defaultQuerystring""Query on first render.
onModeChange(mode: "search" | "ask") => void–Runs when the palette switches between Search and Ask AI.
onClose() => void–Escape on an empty search. Use it to close the surrounding dialog.
placeholder{ search?: string; ask?: string }–Input placeholders per mode.
thinkingLabelstring"Searching your workspace"Status while the model works, before any text arrives.
autoPlaybooleantrue without items and onAskPlays a looping walkthrough while on screen. Stops for good on the first real pointer or key press inside, and never runs with reduced motion.
hotkeybooleanfalseListens for Cmd+K or Ctrl+K on the page and focuses the input.
heightnumber392Height of the results area in px. The palette keeps this size while typing, switching modes and streaming.
elevatedbooleanfalseAdds the floating shadow, for use inside a dialog or popover.
classNamestring–Class on the root.
styleCSSProperties–Inline styles on the root.
aria-labelstring"Command palette"Accessible name of the palette.

AiCommandAction

A suggested next step returned with an answer and run in place.

PropTypeDefaultDescription
idRequiredstring–Stable id.
labelRequiredstring–Row label, such as "Create issue in Linear".
metastring–Quiet detail after the label.
markReactNode–16px brand mark or icon.
run(signal: AbortSignal) => Promise<string | void>–Does the work. Resolve with a done label or nothing; throw to show the error state (Enter retries).
runningLabel / doneLabel / errorLabel / doneMetastring–Labels for each state. doneMeta is where a simulated action says nothing was sent.

AiCommandSource

A document the answer cites as [1], [2] in its text.

PropTypeDefaultDescription
idRequiredstring–Stable id.
titleRequiredstring–Short chip title, such as "billing-api #1893".
descriptionstring–Longer text for the tooltip and screen readers.
markReactNode–14px brand mark or icon.
hrefstring–Opens in a new tab when chosen.
UporDown
Move through results, questions or suggested actions.
Enter
Open the page, run the action, ask the question, or retry a failed answer or action.
Tab
Ask AI about the current query. With an empty query it opens Ask AI with suggested questions; in that state Tab goes back to search.
Escape
From Ask AI, back to search (cancels a streaming answer). In search, clears the query, then calls onClose.
CmdorCtrl+K
Focuses the input when hotkey is on.
  • The input is a combobox that owns a listbox; the active row is announced through aria-activedescendant, so focus never leaves the input.
  • A polite live region announces questions, finished answers, action results and simulated runs ("Simulated, nothing was sent").
  • Status is never color alone: running, done and failed actions change both the icon and the label.
  • Citations and source chips are buttons with full descriptions as their accessible names; hovering or focusing one highlights its partner.
  • The mode toggle uses aria-pressed buttons and keeps input focus.
  • The palette never changes size: the results area has a fixed height and views crossfade in place (100ms out, 160ms in, 2px rise).
  • One highlight glides between rows in 130ms and jumps without travel when the list itself changes, so typing never animates.
  • Answers stream 3 to 7 words every 34ms, each chunk fading in over 140ms; the view follows the text down unless you scroll up.
  • Labels, status icons and footer hints swap with a single crossfade. The done check draws once.
  • Reduced motion: no walkthrough, no chunk fades or travel; the answer reveals in large steps and every state stays.
  • The walkthrough pauses off screen through IntersectionObserver and restarts from a clean state.
  • Fills its container up to 680px and holds up at 360px; long labels and meta truncate with an ellipsis, meta first.
  • Below 540px the navigate hint hides from the footer; source chips wrap.
  • Rows are 40px tall and work with touch; hover only moves the highlight on fine pointers that actually move.
  • Search is a synchronous word match over the items array, fine for a few hundred rows; filter on the server beyond that.
  • Streaming appends one span per chunk; the highlight is positioned directly in the DOM without extra renders.
  • The walkthrough and its timers stop off screen and on unmount; in-flight answers and actions abort through their signals.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Use for a product-wide Cmd+K that can also answer questions; put it inside a dialog with elevated and onClose.
  • Return sources before text with setSources so the chips land first, and cite them in the text as [1], [2].
  • Give each suggested action a run that takes the AbortSignal; return the created object's name ("Created ENG-2481") as the done label.
  • Keep item labels short and put context in meta; use group to section results.
  • For a plain command menu without AI, use command-palette instead.

The full library index for assistants is at /llms.txt.