Skip to content
weftGitHub

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/warp

Usage

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

PropTypeDefaultDescription
valuestring | State<string>—The current value, when you own the state.
busybooleanfalseA run is going: Send becomes Stop.
blockedstring—Why sending is off.
labelstring—Its accessible name. Fields show it; icon-only controls speak it.
placeholderstring—Sample text shown while empty. Never a substitute for a label.
toolsPromptTool[]—Which tool buttons show: attach, slash, mic, web.
modelstring | State<string>—The picked model's id: a state, or a plain value with onModelChange.
modelsModelOption[]—Adds the model chip.
contextContextUse—Adds the context meter.
filesPromptFile[]—Attached files shown as chips above the input, with upload progress.
attachmentsunknown—Your own attachment row, in place of files.
commandsPromptSuggestion[]—What / offers: one suggestion each, with a description.
mentionsPromptSuggestion[]—What @ offers: one suggestion each, with a description.
webbooleanfalseWhether the Web toggle is on.
micbooleanfalseWhether dictation is on: the mic button turns critical.
compactbooleanfalseFor a phone: fewer tools and the short model name.
control((c: PromptControl) => void)—Hands you insert() and focus().
childrenunknown—Shown above the box, usually Suggestions.
EventTypeDefaultDescription
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

RelatedModel pickerContext meterSuggestionsSpeech input