Prompt Input

The composer for a chat: a growing textarea, attached context as removable chips, and a toolbar. Enter sends, Shift+Enter adds a newline, and IME composition is checked first.

src/components/ui/prompt-input.tsx

About

From the component's own source, where it was written.

The composer for a chat: a growing textarea, a row of attached context, and a toolbar.

Enter sends and Shift+Enter inserts a newline — the convention every chat client shares, and the one users have muscle memory for. IME composition is checked first, or the Enter that confirms a Japanese or Chinese candidate would send the message instead.

Install

npx astralyx-ui add prompt-input

Usage

import { PromptInput } from '@/components/ui/prompt-input'

<PromptInput
  context={attached}
  onRemoveContext={remove}
  toolbar={<ContextPicker sources={sources} … />}
  onSubmit={send}
/>

Composer

Change the props and the source below follows.

button.tsx1–86styles.ts1–210
Context used18k / 200k
Props
tsx
<PromptInput
  onSubmit={send}
  context={attached}
  onRemoveContext={remove}
  toolbar={<ContextPicker sources={sources} />}
/>

Examples

With context and a model picker

Streaming

With a budget

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

PromptInput

Extends Omit<ComponentProps<'div'>, 'onSubmit'>.

PropTypeDefaultDescription
defaultValuestring | number | readonly string[]''—
valuestring—Controlled and uncontrolled text.
onValueChange(value: string) => void—Controlled and uncontrolled text.
onSubmit(value: string) => void—Fires on Enter or the send button. Never fires while empty or busy.
placeholderstring'Ask anything…'—
disabledbooleanfalse—
busybooleanfalseSwaps send for stop and blocks submission while a reply is streaming.
onStop() => void—Swaps send for stop and blocks submission while a reply is streaming.
contextContextItem[][]{ id, label, icon?, detail? }, rendered as removable chips above the field.
onRemoveContext(id: string) => void——
toolbarReactNode—Leading toolbar slot — a context picker, a model select, an attach button.
maxLengthnumber—Budget. The counter only appears past 80%, and turns destructive at the limit.
stopLabelstring'Stop'—
sendLabelstring'Send'—

PromptAttachButton

The usual leading toolbar button, for attaching context.

Extends ComponentProps<typeof Button>.

PropTypeDefaultDescription
color'neutral' | 'blue' | 'violet' | 'cyan' | 'green' | 'amber' | 'rose' | 'destructive'——
variant'link' | 'default' | 'colored' | 'secondary' | 'outline' | 'ghost'——
size'sm' | 'default' | 'xs' | 'lg' | 'xl' | 'icon-xs' | 'icon-sm' | 'icon' | 'icon-lg' | 'icon-xl'——
asChildboolean—Render the child element instead of a <button>, keeping the styles.
tintstring—Any CSS colour, used instead of a named color set. Fill, text, tint and border are all derived from it, and every variant keeps working.
labelstring'Attach context'Accessible name and tooltip for the button.

Types

ContextItem

The composer for a chat: a growing textarea, a row of attached context, and a toolbar.

FieldTypeDescription
idstring—
labelstring—
icon?ReactNode—
detail?stringShown after the label, e.g. a line range or a file size.

Behaviour

TopicDetailDescription
keyboardEnter / Shift+EnterSend and newline. Composition events are checked first, so an IME confirming a candidate does not send.