PromptBar
A compact composer with inline sources, commands, models, and dictation.
Usage
The Full Bar
Type @ for sources or / for commands; ↑↓ moves, Enter or Tab picks, and Esc dismisses.
Rounded and Pill shapes
Best Practices
- Attachments are controlled: after
sendthe bar clears only its text, so clear the attachments yourself. - Dictation is presentation only; recognize speech in the host and write the result back with
insert()or thev-model. - Pass brand glyphs through the
#source-iconslot; the component ships no third-party SVGs. - Menus are absolutely positioned against the bar, so an
overflow: hiddenancestor clips them and they never flip; keep the bar out of clipping scroll containers. - When you want a block composer without menus, use
TxChatComposer.
API Reference
Props
| Name | Type | Default | Description |
|---|---|---|---|
modelValue | string | — | The draft; unbound, the bar keeps its own. |
variant | 'rounded' | 'pill' | 'rounded' | Shell and control radius. |
placeholder | string | 'Write a message…' | Placeholder text, and the fallback accessible name. |
ariaLabel | string | — | Accessible name of the textarea; falls back to placeholder. |
disabled | boolean | false | Disables the whole bar. |
submitting | boolean | false | A turn is in flight: blocks sending, never typing. |
sources | PromptBarSource[] | — | Rows of the @ menu; omitting them also hides the + button. |
commands | PromptBarCommand[] | — | Rows of the / menu; name carries its own leading slash. |
attachments | AiAttachment[] | [] | Chips to display; uploads and changes stay with the host. |
models | PromptBarModel[] | [] | Model list; when empty, no model button renders. |
model | string | first entry | Selected model key, for v-model:model. |
dictatable | boolean | false | Renders the dictation button. |
listening | boolean | — | Dictation state, for v-model:listening; recognition stays with the host. |
listeningPlaceholder | string | 'Listening…' | Placeholder while listening. |
minHeight | number | 28 | Collapsed textarea height in px. |
maxHeight | number | 100 | Growth ceiling in px; past it the textarea scrolls. |
sendOnEnter | boolean | true | Enter sends; Shift+Enter breaks the line. |
allowEmptySend | boolean | false | Allows sending with neither text nor attachments. |
Every string is a prop with an English default, overridable one by one: sourcesHintText, commandsHintText, emptyTextFormatter, connectText, connectedText, sendLabel, attachLabel, modelLabel, startDictationLabel, stopDictationLabel, attachmentFallbackLabel, removeAttachmentLabelFormatter.
PromptBarSource is { key, name, desc?, attach?, connectable?, connected? }; PromptBarCommand is { key, name, desc? }; PromptBarModel is { key, name, tag? }.
Events
| Name | Payload | Description |
|---|---|---|
update:modelValue | (value: string) | Fires when the draft changes. |
update:model | (key: string) | Fires when the selected model changes. |
update:listening | (listening: boolean) | Fires when dictation is toggled. |
send | ({ text, attachments }) | Fires on send; the bar clears its text, the host clears attachments. |
attach | () | Fires when the attach row is picked; open your file dialog. |
attachmentRemove | (id: string) | Fires when a chip's remove control is clicked. |
attachmentAdd | (files: File[]) | Fires for pasted or dropped files. |
sourceSelect | (source: PromptBarSource) | Fires after a mention is inserted. |
commandSelect | (command: PromptBarCommand) | Fires after a command is inserted. |
connectToggle | (source: PromptBarSource) | Fires when an unconnected connectable row is activated. |
paste | (event: ClipboardEvent) | The raw paste event; the default isn't prevented. |
focus / blur | (event: FocusEvent) | Fire on textarea focus changes. |
Slots
| Name | Scope | Description |
|---|---|---|
source-icon | { source } | Leading 22×22 glyph of an @ row. |
attachments | { attachments } | Replaces the whole chip strip. |
actions | { send, canSend } | Custom controls just before the send button. |
Exposed Methods
| Name | Type | Description |
|---|---|---|
focus | () => void | Focuses the textarea. |
insert | (text: string) => void | Appends text to the draft, adding a space if needed. |
closeMenus | () => void | Closes the + menu and the model menu. |
menuOpen | boolean | Whether any menu is open. Read-only. |
Using the Token Menu on Its Own
useTokenMenu exports the @ / / parser and menu state machine on their own, with no DOM, for adding mentions to your own input.
import { parseToken, useTokenMenu } from '@talex-touch/tuffex/prompt-bar'
const draft = ref('')
const { menu, rows, activeIndex, engaged, move, engage, dismiss, insert } = useTokenMenu({
draft,
sources,
commands,
})
// Picking a row: replace the pending token, get the new draft back
draft.value = insert(`@${row.name}`)
Sources match as a substring of the name; commands match as a prefix once the slash is stripped (/comp narrows to /compare). parseToken only reads a trailing token at a word boundary, so you@host is never a mention.
Overview
- While an IME is composing, Enter only confirms the candidate; it neither sends nor picks a menu row.
- The menus follow the combobox pattern: the textarea is a
role="combobox"witharia-activedescendant, the menu alistbox; with nothing to offer, it stays a plain text box. - The highlight appears only after a hover or an arrow key, and the first ↑ / ↓ lands on an end of the list; pressing a row never blurs the textarea.
- Activating an unconnected connectable row emits
connectToggleinstead of inserting a mention; once connected, it inserts. - Clicking outside or pressing Esc closes the menus; Esc is consumed only when it closed something, so it otherwise bubbles.
- Under reduced motion the pop and press animations stop, and the three dictation bars freeze rather than vanish.
Technologies
- Adapted from Beautiful UI (© 2026 Shane Levine, MIT).
- Source:
packages/tuffex/packages/components/src/prompt-bar/.