Mischief

19 / Agent UI

Questionnaire

The questions an agent asks before it starts. One at a time, with single or multiple answers, an open answer alongside them, and required ones it will not move past.

Question 1 of 3

Which files should I rename?

I found 41 matches across the repository.

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/questionnaire
import { Questionnaire } from "mischief-ui/questionnaire"
Also installs
  • lucide-react

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/questionnaire/questionnaire.tsx
"use client" import * as React from "react"import { ArrowLeft, ArrowRight, Check, TriangleAlert } from "lucide-react"import { cn } from "@/lib/utils" export type QuestionChoice = {  id: string  label: React.ReactNode  description?: React.ReactNode} export type Question = {  id: string

Usage

const questions = [
  {
    id: "scope",
    prompt: "What should I change?",
    required: true,
    choices: [
      { id: "one", label: "Only this file" },
      { id: "all", label: "Every file that matches" },
    ],
  },
]

export function Clarify() {
  return <Questionnaire questions={questions} onSubmit={start} />
}

The answer shape

Answers are a record of question id to an array of strings, whatever the question. A single-choice question holds one entry, a multiple-choice question holds several, and a freeform answer is the typed text itself. One shape means reading the result never depends on how the question was configured.

{
  "scope": ["invoices"],
  "fields": ["total", "tax", "due-date"],
  "notes": ["Skip anything before 2024"]
}

A question with required set is not satisfied until its array is non-empty, and submission stays blocked until every required question is.

Freeform answers

Every question offers an open text answer by default, because the moment the choices do not cover the case, a fixed list forces a wrong answer. Turn it off for the whole set with freeform={false}, or per question, when the choices really are exhaustive.

<Questionnaire
  questions={questions}
  freeform={false}
  onSubmit={start}
/>
A question may still opt back in with freeform on itself.

Keyboard

  • Number keys pick the matching choice, and are ignored while a text field has focus.
  • Tab reaches every choice and the freeform field in order.
  • Enter submits once the required questions are answered.

API

questionsQuestion[]Prompt, optional description, choices, and flags for multiple, freeform, and required.
answers, defaultAnswers, onAnswersChangeQuestionnaireAnswersChosen choice ids per question, controlled or uncontrolled.
onSubmit(answers: QuestionnaireAnswers) => voidRuns with every answer once the last question is submitted.
freeformbooleanOffers an open answer on every question. Defaults to true, and a question can set its own.
shortcutsbooleanNumber keys pick the choice they label. Defaults to true.
showProgressbooleanShows the position and a progress bar.
previousLabel, nextLabel, skipLabel, submitLabel, requiredMessagestringCopy for the controls and the validation message.

Question

idstringKey this question's answer is stored under.
promptReactNodeThe question itself.
descriptionReactNodeA clarifying line beneath the prompt.
choicesQuestionChoice[]Offered answers. Omit for a purely open question.
multiplebooleanAllows more than one choice.
freeformbooleanOverrides the set-wide setting for this question.
freeformLabel, freeformPlaceholderstring, stringWording for the open answer.
requiredbooleanBlocks submission until answered.

QuestionChoice

idstringWhat lands in the answer array.
labelReactNodeThe choice as shown.
descriptionReactNodeA second line under the choice.

Accessibility

Each question is a fieldset with its prompt as the legend, so the whole question is announced rather than a run of loose options. A single answer uses radios and several uses checkboxes, which brings the right keyboard behaviour without rebuilding it. Position is reported in a polite live region, and a required question that is not answered raises an alert tied to the inputs rather than only colouring them. Number shortcuts are ignored while a freeform answer is being typed.