Installation
npx shadcn@latest add @pixeldosa/ai-form-fillRequires `cn` at `@/lib/utils`. Installing this pulls in Smart Field, Field, AI Action Toolbar and Live Status Line, plus Confidence Meter and Agent Presence transitively. Fetch the whole batch once in `onFill` and have each field's `fetchProposal` read from that result — otherwise a five-field form makes five round trips for one user action.
Usage
"use client";
import { AIFormFill } from "@/components/blocks/ai-form-fill";
const batch = React.useRef<Record<string, SmartFieldProposal>>({});
<AIFormFill
fields={[
{ name: "company", label: "Company", fetchProposal: async () => batch.current.company },
{ name: "industry", label: "Industry", fetchProposal: async () => batch.current.industry },
]}
values={values}
onValueChange={(name, value) => setValues((v) => ({ ...v, [name]: value }))}
onFill={async () => { batch.current = await enrich(values); }}
triggerIcon={<SparkleIcon />}
/>
Fetch the whole batch once in onFill, then have each field's fetchProposal read
from it. Otherwise a five-field form makes five round trips for one user action.
What it refuses to do
Bulk filling makes an "Accept all" button feel obvious. It isn't there, deliberately.
Smart Field exists so a proposed value is reviewed with its confidence and source before it becomes real data. A single click that accepts twelve values across three different confidence tiers silently discards that. So: the request is bulk, the review stays per-field.
That's the honest reading of "nothing commits silently" — not a convenience compromise.
Features
- One request, many proposals. A single token bump makes every field propose together, while each keeps ownership of its own proposal, confidence, undo window and in-flight request.
- Per-field confidence and provenance. A high-confidence company name and a low-confidence headcount guess don't get treated the same way.
- Every field independently reversible — accept, reject, one-step undo, unchanged from Smart Field.
- Status only while working. The surface is completely still whenever the agent isn't running and you're reviewing.
Variants
Ready
Notice the first field has an existing value — filling proposes alongside it rather than overwriting, and nothing changes until accepted.
Legal entity name as it should appear on the invoice.
Examples
Proposing for empty fields only, leaving anything the user already typed alone:
const fields = ALL_FIELDS.filter((f) => !values[f.name]);
Gating the commit behind an approval when writing the form is consequential:
<AIFormFill {...formProps} />
{dirty ? (
<AIApprovalGate
action={`Update the customer record with ${acceptedCount} AI-proposed values`}
reversible
impact={[{ label: "Fields changed", value: String(acceptedCount) }]}
onApprove={save}
onReject={revert}
/>
) : null}
Props
| Prop | Type | Description |
|---|---|---|
fields | FormFillField[] | Required. { name, label, description?, placeholder?, fetchProposal }. |
values | Record<string, string> | Required. Controlled. |
onValueChange | (name, value) => void | Required. |
onFill | () => void | Promise<void> | Runs once per request — fetch the batch here. |
triggerIcon | ReactNode | Required. No icon set is bundled. |
fillLabel | string | Defaults to "Fill with AI". |
status | string | Status text while filling. |
Composes
Smart Field · Field · AI Action Toolbar · Live Status Line — and transitively
Confidence Meter and Agent Presence.
When not to use
A single field is Smart Field used directly. Approving a consequential action is
AI Approval Gate. A full agent run with reasoning and interrupts is
Thinking Experience.
why it's built this way
Engineering Notes
The load-bearing decision is what this block refuses to add. Bulk filling makes an 'Accept all' control feel obvious, and it is exactly wrong: Smart Field exists so that a proposed value is reviewed with its confidence and source before it becomes real data, and a single click that accepts twelve values with three different confidence tiers silently discards that. So the request is bulk and the review stays per-field — which is the honest reading of the system's 'nothing commits silently' rule rather than a convenience compromise. Making fields propose together required a small, consumer-driven addition to Smart Field rather than a workaround here: a `proposeToken` prop whose change requests a proposal. The alternatives were worse — remounting each field to re-fire `autoPropose` would have destroyed focus and in-flight requests, and hoisting proposal state into this block would have taken ownership of confidence, undo and abort away from the component that already does all of it correctly. The token deliberately ignores its first render, so mounting fields with a token present is not a request; that keeps unannounced fetching opt-in behind `autoPropose`, which is the posture the AI tier holds. `onFill` exists so the whole batch is one round trip: without it, a five-field form would make five separate model calls for a single user action, which is both slow and expensive in a way the user never asked for.
how it moves, and when it doesn't
Motion Notes
Almost nothing belongs to this block. Every moving part is inherited: Smart Field's proposal panel expands via the house grid-template-rows transition, Live Status Line swaps its status text and carries Agent Presence's indicator, and AI Action Toolbar supplies the fill button's colour transitions and its busy spinner during the batch request. The block itself adds no entrance animation and no motion of its own, which is the correct outcome for a composition — one rhythm, set once in the motion tokens, applied by the parts rather than re-stated by the whole. The one motion decision made here is that the status line appears only while a fill is actually in flight, so the surface is completely still whenever the agent is not working and the user is reviewing. Under prefers-reduced-motion every inherited transition drops to instant and each composed component handles its own fallback.