Prompt input

Writes the next message to an agent, then sends, queues or stops it.

Examples

Send, queue and stop

support-agent

Refund of $48.00 on ord_4812 is paused while refund-policy reruns.

Nothing sent yet.

Mentions in the prompt

Stacked, for long-form

Files attached

complaint-4812.pdf48 KB
order-export.csvUploading 40%

Waiting for 1 upload

Follow-ups waiting in a queue

3 queued, held because the first one failed

  1. FailedRefund Maya Chen

    Rate limited. The queue waits until you retry, edit or remove it.

  2. QueuedDraft her a reply
  3. QueuedClose the ticket

Shell mode

Shell. Escape to leave

Send, in each state

  • Empty
  • Ready
  • Blocked
  • Streaming
  • Error

Mentions in an answer

support-agent read refund-policy.md and checked run 4812 before it ran /refund. The old runbook runbooks/escalations.md (no longer exists) no longer exists.

Mentions in an editable list

  • refund-policy.md
  • reply-macros.md
  • policies
  • runbooks/escalations.md

States

  • ready

  • submitted

  • streaming

  • error

Usage

When to use it
Use
The person writes the next turn for an agent and may reference tickets, runs or commands.
Use
The agent works for a while and the person may queue a follow-up or stop it.
Not for
A plain multi-line form field. Use Textarea.
Not for
A single-line value. Use Input.

Rules

12 rules
Must
Insert a file, run, prompt release or command as a chip through the @ or / menu or a [label](type:id) token via insert(). Because plain "@run 4812" stays text, so Backspace eats it a letter at a time and the serialized value carries no run id.
Must
Pass status="submitted" while a send is in flight with no byte back; it is already Stop. Because a disabled composer leaves a slow or stuck request with no way to cancel it.
Must
Pass a new generation (the run id) for each run, and settle only the run onStop names. Because without it a stop that lands after the next run began settles the new run.
Must
Append to PromptQueue in FIFO order when the person sends while the agent works; never interrupt the run. Because stopping to send throws away the work the agent has done this turn.
Must
Keep a failed queued message at the head with status="failed" and its error, and stop draining. Because dropping the head loses the message without a word and sends the rest out of order.
Must
Render PromptQueue directly above PromptInput with no gap; they share one line. Because a gap or a wrapper between them breaks the shared line and the queue reads as a second, unrelated box.
Must
Pass disabledReason with what happened and when it changes, plus one action, instead of disabled alone. Because disabled alone leaves a dead box with no word on why or when it returns; the reason becomes its description.
Must
Pass label (default "Prompt"); never rely on the placeholder for the name. Because the editor is a contenteditable textbox named only by label, and aria-placeholder is not a name.
Must
Keep the default inline layout for chat; use layout="stacked" only where the person writes a long brief. Because a two-line box with a toolbar under it spends 40px of the thread on a prompt that is one line most turns.
Must
Return early on isComposing or keyCode 229 before reading Enter, Tab, arrows or Backspace. Because the Enter that confirms a Japanese or Chinese candidate would send a half-written prompt.
Never
Paint chips over a textarea with a mirror overlay. Use PromptInput. Because the overlay drifts from the text on wrap and scroll, and Backspace deletes one letter of a chip.
Never
Disable the composer to block double sends. The editor clears on send and the generation key guards repeats. Because disabled removes the editor from the tab order, so the person cannot write a follow-up or press Escape to stop.

Accessibility

8 checks
The editor is a contenteditable textbox, aria-multiline, named by label; it points at the open list with aria-activedescendant; chips are contenteditable=false.
Enter sends (holds while files upload), Shift+Enter breaks the line.
@ after a space opens resources, / as the whole input opens commands, ! opens shell; arrows or Ctrl+P and Ctrl+N move, Enter or Tab picks.
Escape closes the menu, then leaves shell, then cancels a held send, then stops the run.
Backspace next to a chip removes it whole; Mod+Z restores it; arrows hop over it (mirrored in RTL).
Up in an empty editor edits the last queued message; Alt+Up and Alt+Down move a queue row.
Toolbar buttons are 28px with aria-labels; the editor is 16px so iOS does not zoom.
PromptInput sends on its own and passes type="button" to its Send, so a surrounding form never submits; a bare SendButton is type="submit" while ready.

Edge cases

10 edge cases
Long text and URLs
Wraps; the editor grows to 8 lines (10 stacked) then scrolls, and the buttons stay on the bottom line.
Chips with an attachment, narrow
A long chip truncates; the attachment keeps its name.
Right to left
Layout mirrors; the chip hop reads the editor direction.
Enter during upload
The send is held and fires when the last upload finishes, with Cancel; a failed file blocks it.
Inside a dialog, @ then Escape twice
The first closes the menu, the second closes the dialog.
Paste of 3 lines or 150 characters
Collapses into a "Pasted n lines" chip and is sent in full; Mod+Z collapses it again.
Paste past maxLength
Refused; the status line says how much fits.
Narrow
A toolbar under 320px drops the tools to their own row, and under 384px the status line takes a row of its own; inline stays one row.
Send fails
status="error" shows Send again; the caller keeps the text.
Drag of a type accept refuses
The overlay takes the fail wash and says what it takes, top-left, before the drop.

API

25 props
onSubmit
(value: PromptValue, attachments: Attachment[]) => void
onValueChange
(value: PromptValue) => void
defaultValue
string
draftKey
string
placeholder
string default 'Ask the agent to do something'
waitingCount, onReviewWaiting
number, () => void default 0
layout
'inline' | 'stacked' default 'inline'
label
string default 'Prompt'
status
'ready' | 'submitted' | 'streaming' | 'stopping' | 'error' default 'ready'
onStop, generation
(generation) => void, string | number
disabled, disabledReason, disabledAction
boolean, ReactNode, ReactNode default false
mentions, commands
MentionItem[], CommandItem[] default []
shell, mode, onModeChange
boolean, 'normal' | 'shell', (mode) => void default false
attachments, onAttach, onRemoveAttachment, onRetryAttachment, accept
Attachment[], (files: File[]) => void, (id) => void, (id) => void, string default []
maxLength
number default 100000
tools, meta, voice
ReactNode, ReactNode, Omit<PromptInputMicProps, 'size'>
onEditQueued, onSendQueuedNow
() => boolean, () => void
PromptQueue items
{id, text, status?: 'queued' | 'sending' | 'failed', error?}[] default required
PromptQueue onSendNow, onRetry, onEdit, onRemove, onMove
(id) => void, (id) => void, (id, text) => void, (id) => void, (id, index) => void
PromptQueue editingId, onEditingChange
string | null, (id) => void
PromptQueue returnFocus, labels
() => void, PromptQueueLabels
labels
PromptInputLabels default English words
SendButton status, onStop, generation, empty, blockedReason
SendStatus, (generation) => void, string | number, boolean, string default 'ready'
Mention type, children, missing, onRemove
MentionType, ReactNode, boolean, () => void default required, required
ref
Ref<PromptInputHandle>

Tokens

25 tokens
--line-control
oklch(var(--ink-c) / 0.16)
--line-frame
oklch(var(--ink-c) / var(--line-frame-alpha))
--line-row
oklch(var(--ink-c) / 0.08)
--surface-card
oklch(var(--grey-c))
--code-prompt
oklch(var(--ink-c) / 0.40)
--border-focus
var(--ink)
--type-body
340 14px/20px var(--font-sans)
--type-code
400 13px/20px var(--font-mono)
--focus-halo
0 0 0 3px oklch(var(--ink-c) / 0.08)
--elev-control
0 1px 2px -1px oklch(var(--text-on-field-c) / calc(var(--shadow-alpha) * 0.6))
--text-1
var(--ink)
--text-3
oklch(var(--ink-c) / 0.66)
--fail-wash
oklch(94.5% 0.027 26)
--fail-text
oklch(51% 0.180 26)
--elev-overlay
0 0 0 1px var(--line-frame), 0 12px 24px -12px oklch(var(--text-on-field-c) / var(--shadow-alpha))
--surface-hover
oklch(var(--ink-c) / 0.05)
--control-sm
28px
--type-label-sm
400 12px/16px var(--font-sans)
--type-tag
var(--type-code-sm)
--text-2
oklch(var(--ink-c) / 0.80)
--dur-fast
120ms
--dur-base
160ms
--ease-step
cubic-bezier(0.2, 0, 0, 1)
--attachment-max
256px
--popup-max
320px

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.