Components/ChatComposer

ChatComposer

A message composer for AI chat, with an optional context tray.

VerifiedSince 1.0.0
<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 send as intent: clear modelValue once the request succeeds, and keep the text on failure for a retry.
  • Set submitting while a request is in flight to prevent duplicate sends.
  • Use attachments for status chips only; keep upload progress, removal, and retry in the host or the attachments slot.
  • Keep tray content to one line, equally tall on both sides, so a swap doesn't resize the box; use a muted TxModeChip for tray actions.
  • Move the tray when the context changes ("Connect apps" becoming "Select a project"); never loop the swap as decoration.

API Reference

Props

NameTypeDefaultDescription
modelValuestring''Textarea text, bound with v-model.
placeholderstring'Message…'Placeholder text.
ariaLabelstring-Accessible name of the textarea; falls back to placeholder.
disabledbooleanfalseDisables input, sending, and attachment actions.
submittingbooleanfalseMarks a request in flight and blocks sending.
allowAttachmentWhileSubmittingbooleanfalseKeeps attachment actions available while submitting.
minRowsnumber3Resting height in rows; the textarea grows with its content.
maxRowsnumber6Growth cap in rows, then it scrolls; never below minRows.
sendOnEnterbooleantrueEnables keyboard sending.
sendOnMetaEnterbooleantrueRequires Meta/Ctrl+Enter to send from the keyboard.
allowEmptySendbooleanfalseAllows sending attachments with empty text.
sendButtonTextstring'Send'Accessible name of the send icon button.
showAttachmentButtonbooleanfalseShows the default + attachment button.
attachmentButtonTextstring'Attach'Accessible name of the + attachment button.
attachmentsChatComposerAttachment[][]Attachment chips above the textarea.
trayPlacement'top' | 'bottom''bottom'Puts the tray above or below the card; a change runs the swap motion.
trayLabelstring-When set, the tray renders as a role="group" with this name.

Events

NamePayloadDescription
update:modelValuestringFires 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.
pasteClipboardEventForwards the textarea's paste.
attachmentAddFile[]Fires for pasted or dropped files; the host owns the upload.
focusFocusEventForwards the textarea's focus.
blurFocusEventForwards the textarea's blur.

Slots

NameScopeDescription
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.

Types

EXAMPLE.TS
interface ChatComposerAttachment {
  id: string // chip key
  label: string // chip text; truncated with an ellipsis
  kind?: string // type tag after the text, shown in uppercase
  pending?: boolean // still processing; tints the chip amber
}

CSS Variables

VariableDefaultDescription
--tx-chat-composer-radius18pxCorner radius shared by the shell and the card.

Overview

  • Sending needs non-empty trimmed text, or attachments with allowEmptySend. disabled and submitting both block it.
  • Attachment actions (button, paste, file drop) are blocked by disabled, and by submitting unless allowAttachmentWhileSubmitting is set.
  • With sendOnMetaEnter, Meta/Ctrl+Enter sends; without it, Enter sends and Shift+Enter inserts a newline.
  • The textarea grows from minRows lines and scrolls at maxRows (field-sizing: content); browsers without it stay at minRows.
  • 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 tray slot. 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