Prompt
The box people type to the assistant in, with tools, commands and a model chip.
SourcePackage @kerf-js/veneer
Preview
tsx
import { state } from '@kerf-js/core'import { Prompt, Stack, Text } from '@kerf-js/veneer'import type { PromptFile } from '@kerf-js/veneer'import type { ModelOption, PromptSuggestion, ContextUse} from '@kerf-js/veneer'export function Composer() { const sent = state('') const files = state<PromptFile[]>([]) const web = state(false) const attach = (list: File[]) => (files.value = [ ...files.value, ...list.map((f) => ({ id: f.name, name: f.name, })), ]) const remove = (id: string) => (files.value = files.value.filter((f) => f.id !== id)) 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={attach} onRemoveFile={remove} onSubmit={(text) => (sent.value = text)} /> </Stack> )}const PEOPLE = [ { id: 'mara', name: 'Mara Lindqvist', email: 'mara@kerf.dev', role: 'Owner', team: 'Platform', initials: 'ML', }, { id: 'jun', name: 'Jun Tanaka', email: 'jun@kerf.dev', role: 'Admin', team: 'Platform', initials: 'JT', }, { id: 'priya', name: 'Priya Raman', email: 'priya@kerf.dev', role: 'Member', team: 'Web', initials: 'PR', }, { id: 'tomas', name: 'Tomás Oliveira', email: 'tomas@kerf.dev', role: 'Member', team: 'Web', initials: 'TO', }, { id: 'lena', name: 'Lena Fischer', email: 'lena@kerf.dev', role: 'Member', team: 'Data', initials: 'LF', }, { id: 'noor', name: 'Noor Haddad', email: 'noor@kerf.dev', role: 'Viewer', team: 'Data', initials: 'NH', }, { id: 'sam', name: 'Sam Okafor', email: 'sam@kerf.dev', role: 'Member', team: 'Mobile', initials: 'SO', }, { id: 'ines', name: 'Inés Vidal', email: 'ines@kerf.dev', role: 'Viewer', team: 'Mobile', initials: 'IV', },]const MODELS: ModelOption[] = [ { id: 'deep', name: 'Kerf Deep', short: 'Deep', provider: 'Hosted', description: 'Long investigations', capabilities: ['tools', 'logs'], context: '200k', cost: '$$$', }, { id: 'swift', name: 'Kerf 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 @kerf-js/veneerUsage
tsx
import { Prompt } from '@kerf-js/veneer'Busy
Send becomes Stop. What you send meanwhile goes to onQueue.
tsx
import { state } from '@kerf-js/core'import { Prompt, Stack, Text } from '@kerf-js/veneer'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 '@kerf-js/veneer'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 '@kerf-js/veneer'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. |