<script setup lang="ts">
import { ref } from "vue";
import { MessageComposer, type MessageComposerPayload } from "@dicehub/kappa/blocks/message-composer";
const text = ref("");
const files = ref<File[]>([]);
const messages = ref<MessageComposerPayload[]>([]);
function addMessage(payload: MessageComposerPayload) {
messages.value.push(payload); // Local example; connect your application here.
text.value = "";
files.value = [];
}
</script>
<template>
<MessageComposer v-model="text" v-model:files="files" @send="addMessage" />
</template>Installation
import { MessageComposer } from "@dicehub/kappa";
// Or: "@dicehub/kappa/blocks/message-composer"Import the Kappa theme once in the application. The block composes Input Area, File Upload, Attachment, and Button. It needs no extra dependency.
Composition
The growing textarea uses Ark Field through Input Area. File choice, drop, paste, validation, and duplicate detection use Ark UI File Upload. Attachment rows show the file name and size with a keyboard-accessible removal button.
This block edits plain text and File objects. It emits send and cancel requests. The application owns persistence, request cancellation, delivery state, and authentication. The block does not send network requests or render message history. This version has no rich text or mention syntax.
Kappa changes the File Upload dropzone role to a group so that message editing keeps native textarea semantics. Attachment validation is scoped to this composer; it does not block file drops elsewhere in the document.
Examples
Attachment and text limits
File choice, paste, and drop use the same Ark UI validation. A failed file leaves accepted attachments and the text in place.
<MessageComposer v-model="text" v-model:files="files"
:max-length="80" :max-files="2" :max-file-size="1024"
:accept="['text/plain', 'text/csv', 'application/pdf']"
@send="sendMessage" @file-reject="reportRejectedFiles" />Pending, cancellation, and retry
The application owns the pending state and request cancellation. An error or cancellation keeps the draft. Clear text and files only after success.
<MessageComposer v-model="text" v-model:files="files"
:pending="requestPending" :disabled="!canWrite" :error="sendError"
@send="startRequest" @cancel="abortRequest" />Text-only comments
Disable attachment controls and reserve Enter for newlines. The send button remains the explicit submit action.
<MessageComposer v-model="comment" :allow-attachments="false" :send-on-enter="false"
:labels="{ label: 'Comment', send: 'Add comment' }" @send="addComment" />Draft and request state
Use v-model for text and v-model:files for File arrays. Without these props, defaultValue and defaultFiles initialize local state. Updates emit a new File array and do not mutate the supplied array.
send emits { text, files }. Text keeps its exact whitespace and newlines; only the empty-draft check trims it. Send does not clear the draft. After a successful request, clear the controlled values or call clear() on an uncontrolled composer. Set pending when the request starts to lock editing and prevent more sends.
While pending, Cancel send emits cancel once per activation. The application must abort its own request and clear pending. An error displays feedback without blocking a retry. When pending ends, focus returns to the textarea only if focus stayed in the composer or its former action left focus on the page body.
New files are validated by Ark UI on file choice, paste, and drop. A batch that exceeds maxFiles is rejected as a batch; with maxFiles=1, a valid new file replaces the current one. File arrays supplied by the application through files/defaultFiles are trusted inputs: validate their type before supplying them. Text length, total file count, and file sizes are checked again before send, including after limits change.
API
modelValue?: string, defaultValue?: string, files?: File[], and defaultFiles?: File[] define the draft. The empty defaults are an empty string and array. pending and disabled default to false. Disabled blocks all editor actions; pending keeps the cancel action available.
sendOnEnter, allowAttachments, and allowAttachmentsOnly default to true. A whitespace-only draft without files cannot send. Set allowAttachmentsOnly=false to require message text. Set allowAttachments=false for comments; clear any supplied files first.
maxLength defaults to 4000 UTF-16 code units, matching native textarea length semantics. The editor permits longer input so that the user can correct it; send stays disabled. maxFiles defaults to 5. maxFileSize defaults to 10 MiB per file. accept preserves the Ark File Upload MIME type, MIME array, or MIME-to-extension map API. Empty accept allows all file types.
labels replaces visible and assistive text: label, placeholder, attach, send, sending, cancel, hint, buttonHint, attachments, characters, drop, dismissErrors, and invalidFiles. The removeFile(file), rejectedFile(file, reasons), and tooLong(limit) functions format dynamic labels. Override rejectedFile for localized rejection messages.
id sets the textarea ID. textareaProps forwards native attributes and listeners such as name, autocomplete, spellcheck, aria-label, and keydown. The block owns value, rows, disabled, readonly, and invalid bindings. Its described-by IDs are appended to a supplied aria-describedby. Root attributes and listeners reach the outer wrapper.
Events: update:modelValue(string), update:files(File[]), send(MessageComposerPayload), cancel(), and fileReject(FileUploadFileRejectDetails). The exposed MessageComposerApi has focus(), clear(), and send(). Clear and send respect pending and disabled.
Slots: context adds reply or destination context above the editor; hint replaces the keyboard hint. --kappa-message-composer-max-height sets the text input height limit (default 15rem). Root data attributes are data-slot=message-composer, data-pending, and data-disabled.
Import MessageComposerProps, MessageComposerPayload, MessageComposerLabels, MessageComposerEmits, MessageComposerApi, and MessageComposerSlots from the same root or block entrypoint. The package export wildcard covers the block path.
Accessibility
The visible label names the textarea. Validation and the character count are associated with it. Plain Enter sends; Shift+Enter adds a line. Modified Enter, IME composition, and the keyCode 229 composition fallback keep native input behavior. Holding Enter does not send repeated messages. With sendOnEnter=false, use the send button.
Tab reaches the textarea, each attachment removal button, Attach files, and Send message or Cancel send. Removing a file returns focus to the textarea. Paste keeps ordinary text insertion; pasted files go through the same validation as chosen files. Mixed file-and-text clipboards preserve the text paste. Pending leaves the textarea readable and focusable; disabled uses native disabled controls.
Error and pending messages use live status semantics. The input surface has visible focus, light and dark token values, and reduced-motion support. File objects are not read for previews, so the block allocates no object URLs.