Components/PromptBar

PromptBar

A compact composer with inline sources, commands, models, and dictation.

VerifiedSince 0.3.9

Usage

The Full Bar

Type @ for sources or / for commands; ↑↓ moves, Enter or Tab picks, and Esc dismisses.

Rounded and Pill shapes

Loading demo...

Best Practices

  • Attachments are controlled: after send the 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 the v-model.
  • Pass brand glyphs through the #source-icon slot; the component ships no third-party SVGs.
  • Menus are absolutely positioned against the bar, so an overflow: hidden ancestor 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

NameTypeDefaultDescription
modelValuestring—The draft; unbound, the bar keeps its own.
variant'rounded' | 'pill''rounded'Shell and control radius.
placeholderstring'Write a message…'Placeholder text, and the fallback accessible name.
ariaLabelstring—Accessible name of the textarea; falls back to placeholder.
disabledbooleanfalseDisables the whole bar.
submittingbooleanfalseA turn is in flight: blocks sending, never typing.
sourcesPromptBarSource[]—Rows of the @ menu; omitting them also hides the + button.
commandsPromptBarCommand[]—Rows of the / menu; name carries its own leading slash.
attachmentsAiAttachment[][]Chips to display; uploads and changes stay with the host.
modelsPromptBarModel[][]Model list; when empty, no model button renders.
modelstringfirst entrySelected model key, for v-model:model.
dictatablebooleanfalseRenders the dictation button.
listeningboolean—Dictation state, for v-model:listening; recognition stays with the host.
listeningPlaceholderstring'Listening…'Placeholder while listening.
minHeightnumber28Collapsed textarea height in px.
maxHeightnumber100Growth ceiling in px; past it the textarea scrolls.
sendOnEnterbooleantrueEnter sends; Shift+Enter breaks the line.
allowEmptySendbooleanfalseAllows 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

NamePayloadDescription
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

NameScopeDescription
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

NameTypeDescription
focus() => voidFocuses the textarea.
insert(text: string) => voidAppends text to the draft, adding a space if needed.
closeMenus() => voidCloses the + menu and the model menu.
menuOpenbooleanWhether 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" with aria-activedescendant, the menu a listbox; 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 connectToggle instead 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/.
查看源码
packages/tuffex/packages/components/src/prompt-bar/index.ts