Prompt
The box people type to the assistant in, with tools, commands and a model chip.
SourcePackage @weft-js/warp
Preview
tsx
import { state } from '@weft-js/core'import { Prompt, Stack, Text } from '@weft-js/warp'import type { PromptFile } from '@weft-js/warp'import type { ContextUse, ModelOption, PromptSuggestion } from '@weft-js/warp'export function Composer() { const sent = state('') const files = state<PromptFile[]>([]) const web = state(false) return ( <Stack gap="related"> <Text role="caption" subtle>{sent.value ? `Sent: ${sent.value}` : 'Type / for commands or @ for people.'}</Text> <Prompt placeholder="Ask about a deploy" tools={['attach', 'slash', 'web']} models={MODELS} model="swift" commands={COMMANDS} mentions={MENTIONS} context={CONTEXT} files={files.value} web={web.value} onWebChange={on => (web.value = on)} onAttach={list => (files.value = [...files.value, ...list.map(f => ({ id: f.name, name: f.name }))])} onRemoveFile={id => (files.value = files.value.filter(f => f.id !== id))} onSubmit={text => (sent.value = text)} /> </Stack> )}const PEOPLE = [ { id: 'mara', name: 'Mara Lindqvist', email: 'mara@weft.dev', role: 'Owner', team: 'Platform', initials: 'ML' }, { id: 'jun', name: 'Jun Tanaka', email: 'jun@weft.dev', role: 'Admin', team: 'Platform', initials: 'JT' }, { id: 'priya', name: 'Priya Raman', email: 'priya@weft.dev', role: 'Member', team: 'Web', initials: 'PR' }, { id: 'tomas', name: 'Tomás Oliveira', email: 'tomas@weft.dev', role: 'Member', team: 'Web', initials: 'TO' }, { id: 'lena', name: 'Lena Fischer', email: 'lena@weft.dev', role: 'Member', team: 'Data', initials: 'LF' }, { id: 'noor', name: 'Noor Haddad', email: 'noor@weft.dev', role: 'Viewer', team: 'Data', initials: 'NH' }, { id: 'sam', name: 'Sam Okafor', email: 'sam@weft.dev', role: 'Member', team: 'Mobile', initials: 'SO' }, { id: 'ines', name: 'Inés Vidal', email: 'ines@weft.dev', role: 'Viewer', team: 'Mobile', initials: 'IV' },]const MODELS: ModelOption[] = [ { id: 'deep', name: 'weft Deep', short: 'Deep', provider: 'Hosted', description: 'Long investigations', capabilities: ['tools', 'logs'], context: '200k', cost: '$$$' }, { id: 'swift', name: 'weft Swift', short: 'Swift', provider: 'Hosted', description: 'Fast, good at config', capabilities: ['tools'], context: '200k', cost: '$$' }, { id: 'local', name: 'On-prem small', short: 'Small', provider: 'Your cluster', description: 'Stays on your network', context: '32k', cost: '$' },]const COMMANDS: PromptSuggestion[] = [ { value: '/logs', description: 'Read a deploy\'s log' }, { value: '/rollback', description: 'Go back to an earlier deploy' }, { value: '/status', description: 'Show every project\'s status' },]const MENTIONS: PromptSuggestion[] = PEOPLE.slice(0, 4).map(p => ({ value: `@${p.id}`, description: p.name }))const CONTEXT: ContextUse = { used: 46000, limit: 200000, parts: [{ label: 'Logs', tokens: 22000 }, { label: 'History', tokens: 16000 }, { label: 'Tools', tokens: 8000 }] }Installation
npm install @weft-js/warpUsage
tsx
import { Prompt } from '@weft-js/warp'Busy
Send becomes Stop. What you send meanwhile goes to onQueue.
tsx
import { state } from '@weft-js/core'import { Prompt, Stack, Text } from '@weft-js/warp'export function Busy() { const busy = state(true) const queue = state<string[]>([]) return ( <Stack gap="related"> <Text role="caption" subtle>{busy.value ? 'Running. Messages wait in line.' : 'Stopped.'} {queue.value.length} queued.</Text> <Prompt busy={busy.value} tools={[]} onStop={() => (busy.value = false)} onSubmit={() => (busy.value = true)} onQueue={text => (queue.value = [...queue.value, text])} /> </Stack> )}Blocked
blocked says why Send is off, and the button stays focusable.
tsx
import { Prompt } from '@weft-js/warp'export function Blocked() { return ( <Prompt blocked="You're offline" value="Roll back mobile-bff" tools={[]} /> )}Uploading a file
A file in files with a progress between 0 and 1 shows an upload still under way.
tsx
import { Prompt } from '@weft-js/warp'export function Uploading() { return ( <Prompt value="What does this log say?" tools={['attach']} files={[{ id: 'log', name: 'd-4819-build.log', progress: 0.6 }]} /> )}When to use
Use it for
- The message box of any assistant
Reach for something else
- Textarea, when it is an ordinary form field
Accessibility
- Enter
- Sends
- Shift+Enter
- Adds a line
- Escape
- Stops a running answer
- /or@
- Opens commands or people
- UpandDown
- Move through the open list
- Send and Stop are one button, so focus never drops when a run starts or ends.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | State<string> | — | The current value, when you own the state. |
busy | boolean | false | A run is going: Send becomes Stop. |
blocked | string | — | Why sending is off. |
label | string | — | Its accessible name. Fields show it; icon-only controls speak it. |
placeholder | string | — | Sample text shown while empty. Never a substitute for a label. |
tools | PromptTool[] | — | Which tool buttons show: attach, slash, mic, web. |
model | string | State<string> | — | The picked model's id: a state, or a plain value with onModelChange. |
models | ModelOption[] | — | Adds the model chip. |
context | ContextUse | — | Adds the context meter. |
files | PromptFile[] | — | Attached files shown as chips above the input, with upload progress. |
attachments | unknown | — | Your own attachment row, in place of files. |
commands | PromptSuggestion[] | — | What / offers: one suggestion each, with a description. |
mentions | PromptSuggestion[] | — | What @ offers: one suggestion each, with a description. |
web | boolean | false | Whether the Web toggle is on. |
mic | boolean | false | Whether dictation is on: the mic button turns critical. |
compact | boolean | false | For a phone: fewer tools and the short model name. |
control | ((c: PromptControl) => void) | — | Hands you insert() and focus(). |
children | unknown | — | Shown above the box, usually Suggestions. |
| Event | Type | Default | Description |
|---|---|---|---|
onValueChange | ((v: string) => void) | — | Called with the draft on every keystroke. |
onSubmit | ((text: string) => void) | — | Called with the values when submitted. |
onStop | (() => void) | — | Pressing Stop or Escape while busy. Stop keeps what arrived. |
onQueue | ((text: string) => void) | — | A message sent while busy goes to the queue. |
onModelChange | ((id: string) => void) | — | Called with the id of the model picked. |
onCompact | (() => void) | — | Called from the context meter's Compact: summarise the conversation to free room. |
onAttach | ((files: File[]) => void) | — | Called with the files picked or dropped. |
onRemoveFile | ((id: string) => void) | — | Called with a file's id when its chip is removed. |
onWebChange | ((on: boolean) => void) | — | Called with the Web toggle's new state. |
onMic | (() => void) | — | Called when the mic button is pressed. |
Parts
Every element carries a stable name, so you can style it from your own CSS: [data-scope="prompt"] [data-part="files"]
filesfilesr-onlyhintrowlabelspacersendgroupcmd