ChatComposer
A message composer for AI chat, with an optional context tray.
<script>
import { ref } from 'vue'
const text = ref('')
const sent = ref<string[]>([])
function onSend(payload: { text: string }) {
sent.value.unshift(payload.text)
text.value = ''
}
</script>Usage
Basic
Loading demo...
Tray Placement
The tray slot renders a tray outside the card; trayPlacement puts it below the card (the default) or above it.
Loading demo...
With a Mode Chip
Put a ModeChip in the toolbar-left slot.
Loading demo...
Best Practices
- Treat
sendas intent: clearmodelValueonce the request succeeds, and keep the text on failure for a retry. - Set
submittingwhile a request is in flight to prevent duplicate sends. - Use
attachmentsfor status chips only; keep upload progress, removal, and retry in the host or theattachmentsslot. - Keep tray content to one line, equally tall on both sides, so a swap doesn't resize the box; use a muted
TxModeChipfor tray actions. - Move the tray when the context changes ("Connect apps" becoming "Select a project"); never loop the swap as decoration.
API Reference
Props
| Name | Type | Default | Description |
|---|---|---|---|
modelValue | string | '' | Textarea text, bound with v-model. |
placeholder | string | 'Message…' | Placeholder text. |
ariaLabel | string | - | Accessible name of the textarea; falls back to placeholder. |
disabled | boolean | false | Disables input, sending, and attachment actions. |
submitting | boolean | false | Marks a request in flight and blocks sending. |
allowAttachmentWhileSubmitting | boolean | false | Keeps attachment actions available while submitting. |
minRows | number | 3 | Resting height in rows; the textarea grows with its content. |
maxRows | number | 6 | Growth cap in rows, then it scrolls; never below minRows. |
sendOnEnter | boolean | true | Enables keyboard sending. |
sendOnMetaEnter | boolean | true | Requires Meta/Ctrl+Enter to send from the keyboard. |
allowEmptySend | boolean | false | Allows sending attachments with empty text. |
sendButtonText | string | 'Send' | Accessible name of the send icon button. |
showAttachmentButton | boolean | false | Shows the default + attachment button. |
attachmentButtonText | string | 'Attach' | Accessible name of the + attachment button. |
attachments | ChatComposerAttachment[] | [] | Attachment chips above the textarea. |
trayPlacement | 'top' | 'bottom' | 'bottom' | Puts the tray above or below the card; a change runs the swap motion. |
trayLabel | string | - | When set, the tray renders as a role="group" with this name. |
Events
| Name | Payload | Description |
|---|---|---|
update:modelValue | string | Fires on input. |
send | { text: string } | Fires with trimmed text when sending is allowed. |
attachmentClick | - | Fires when the attachment button is clicked and attaching is allowed. |
paste | ClipboardEvent | Forwards the textarea's paste. |
attachmentAdd | File[] | Fires for pasted or dropped files; the host owns the upload. |
focus | FocusEvent | Forwards the textarea's focus. |
blur | FocusEvent | Forwards the textarea's blur. |
Slots
| Name | Scope | Description |
|---|---|---|
tray | - | Tray content outside the card. |
attachments | { attachments } | Replaces the default attachment chips. |
toolbar | { send, disabled, attachmentClick } | Replaces the default action row. |
toolbar-left | { disabled } | Adds content to the left of the default action row. |
actions | { send, disabled } | Adds content before the send button. |
footer | - | Content inside the card, below the action row. |
CSS Variables
| Variable | Default | Description |
|---|---|---|
--tx-chat-composer-radius | 18px | Corner radius shared by the shell and the card. |
Overview
- Sending needs non-empty trimmed text, or attachments with
allowEmptySend.disabledandsubmittingboth block it. - Attachment actions (button, paste, file drop) are blocked by
disabled, and bysubmittingunlessallowAttachmentWhileSubmittingis set. - With
sendOnMetaEnter, Meta/Ctrl+Enter sends; without it, Enter sends and Shift+Enter inserts a newline. - The textarea grows from
minRowslines and scrolls atmaxRows(field-sizing: content); browsers without it stay atminRows. - The root is the shell, where fallthrough classes and attributes land; the input card is
.tx-chat-composer__card. Adding or removing a tray never recreates the textarea, so focus and IME state survive. - The tray renders only with the
trayslot. Focus inside the leaving tray moves to the textarea; under reduced motion or without the Web Animations API, the swap lands at once.
Technologies
- On a swap the old tray fades out through a CSS transition; the card slide and shell height are a Web Animations FLIP: 70ms delay, 450ms,
cubic-bezier(0.65, 0.16, 0.1, 0.88). - Motion reference: @flohoeller's Chatbox component clip.
- Source:
packages/tuffex/packages/components/src/chat/src/TxChatComposer.vue.
查看源码
packages/tuffex/packages/components/src/chat-composer/index.ts