Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions articles/components/message-input/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ include::{root}/frontend/demo/component/messages/react/message-basic.tsx[render,
endif::[]
--

== File Attachments

Use the <<../upload/modular-upload#,Modular Upload>> components to add file attachment support to Message Input. Place an [classname]`UploadButton` alongside the input and an [classname]`UploadFileList` above it to show pending attachments. When the user submits a message, clear the file list using [methodname]`clearFileList()` on the [classname]`UploadManager`.

[.example,themes="lumo,aura"]
--
ifdef::flow[]
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/component/messages/MessageInputUpload.java[render,tags=snippet,indent=0,group=Flow]
----
endif::[]
--

The upload button opens a file picker. Selected files appear in the file list above the input. When the user sends the message, the submit listener processes the pending files and clears the attachment list.

For AI-powered chat applications, see <<{articles}/flow/ai-support/file-attachments#,File Attachments in AI Support>>, which automates this integration using the [classname]`AIOrchestrator`.


== Related Components

[cols="1,2"]
Expand All @@ -78,6 +97,8 @@ endif::[]

|<<../message-list#,Message List>>|Show a list of messages.

|<<../upload/modular-upload#,Modular Upload>>|Add file attachment support to the input.

|===


Expand Down
3 changes: 3 additions & 0 deletions articles/components/message-list/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ endif::[]
Each attachment has a name, a URL, and a MIME type. The MIME type determines how the attachment is rendered: types starting with `image/` are shown as thumbnail previews, while all other types are shown as file icons.
You can listen for attachment click events to handle downloads or other actions.

To let users upload file attachments, use <<../upload/modular-upload#,Modular Upload>> components with <<../message-input#file-attachments,Message Input>>.


== Usage for AI Chats

Expand Down Expand Up @@ -173,6 +175,7 @@ include::{root}/frontend/themes/docs/screen-reader-only.css[]
|Component |Usage recommendations

|<<../message-input#,Message Input>>|Allow users to author and send messages.
|<<../upload/modular-upload#,Modular Upload>>|Add file upload support for attachments.
|<<../markdown#,Markdown>>|Standalone component for rendering Markdown content.

|===
Expand Down
20 changes: 20 additions & 0 deletions articles/components/upload/modular-upload.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,23 @@
--


== Chat Input with Attachments

The modular design makes it straightforward to integrate upload functionality into a <<../message-input#,Message Input>> component for a chat-with-attachments experience. Place an [classname]`UploadButton` next to the input and an [classname]`UploadFileList` above it. On submit, process the pending files and call [methodname]`clearFileList()` to reset.

[.example,themes="lumo,aura"]
--
ifdef::flow[]
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/component/messages/MessageInputUpload.java[render,tags=snippet,indent=0,group=Flow]
----
endif::[]
--

For AI-powered chat, the <<{articles}/flow/ai-support/file-attachments#,AIOrchestrator>> can automate this wiring.

Check failure on line 152 in articles/components/upload/modular-upload.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'AIOrchestrator'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'AIOrchestrator'?", "location": {"path": "articles/components/upload/modular-upload.adoc", "range": {"start": {"line": 152, "column": 73}}}, "severity": "ERROR"}


== Configuration

The [classname]`UploadManager` accepts an <<file-handling#,upload handler>> for processing files, and shares most of its configuration API with the standard <<index#,Upload>> component -- including <<index#upload-restrictions,upload restrictions>> (file count, size, and format), <<index#auto-upload,auto-upload>>, and <<index#listeners,event listeners>>.
Expand Down Expand Up @@ -207,6 +224,9 @@
|<<index#,Upload>>
|The default Upload component with built-in drop zone, button, and file list.

|<<../message-input#,Message Input>>
|Compose with upload components for chat-with-attachments.

|<<../progress-bar#,Progress Bar>>
|Component for showing task completion progress.

Expand Down
2 changes: 2 additions & 0 deletions articles/flow/ai-support/file-attachments.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,6 @@ var orchestrator = AIOrchestrator
.build();
----

For a manual approach without the [classname]`AIOrchestrator`, see <<{articles}/components/message-input#file-attachments,File Attachments in Message Input>>.

endif::flow[]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example doesn't work, upload button and the file list are not rendered / registered as custom components.

Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.vaadin.demo.component.messages;

import java.time.Instant;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;

import com.vaadin.demo.DemoExporter; // hidden-source-line
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.messages.MessageInput;
import com.vaadin.flow.component.messages.MessageList;
import com.vaadin.flow.component.messages.MessageListItem;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.upload.UploadButton;
import com.vaadin.flow.component.upload.UploadFileList;
import com.vaadin.flow.component.upload.UploadFileListVariant;
import com.vaadin.flow.component.upload.UploadManager;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.streams.UploadHandler;

@Route("message-input-upload")
public class MessageInputUpload extends Div {

// Pending files, keyed by file name
private final Map<String, byte[]> pendingFiles = new LinkedHashMap<>();

public MessageInputUpload() {
// tag::snippet[]
var handler = UploadHandler.inMemory((metadata, data) -> {
pendingFiles.put(metadata.fileName(), data);
});

var manager = new UploadManager(this, handler);
manager.setMaxFiles(5);
manager.setMaxFileSize(10L * 1024 * 1024); // 10 MB

// Remove files from the pending map when the user removes them
manager.addFileRemovedListener(event -> {
pendingFiles.remove(event.getFileName());
});

var uploadButton = new UploadButton(manager);

var fileList = new UploadFileList(manager);
fileList.addThemeVariants(UploadFileListVariant.THUMBNAILS);
fileList.setWidthFull();

var messageInput = new MessageInput();
var messageList = new MessageList();

messageInput.addSubmitListener(event -> {
var message = new MessageListItem(event.getValue(),
Instant.now(), "You");
message.setUserColorIndex(1);

// Add pending files as attachments
for (var entry : pendingFiles.entrySet()) {
message.addAttachment(new MessageListItem.Attachment(
entry.getKey(), "#", "application/octet-stream"));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part doesn't look helpful. It's not clear how to convert something received from the upload handler into a URL that you can provide here. Also the mime type doesn't match what was uploaded.

}

var items = new ArrayList<>(messageList.getItems());
items.add(message);
messageList.setItems(items);

// Clear pending attachments
pendingFiles.clear();
manager.clearFileList();
});

var inputLayout = new HorizontalLayout(uploadButton, messageInput);
inputLayout.setWidthFull();
inputLayout.expand(messageInput);
inputLayout.setAlignItems(FlexComponent.Alignment.END);

var layout = new VerticalLayout(messageList, fileList, inputLayout);
layout.expand(messageList);
layout.setSizeFull();
// end::snippet[]

setSizeFull(); // hidden-source-line
add(layout);
}

public static class Exporter // hidden-source-line
extends DemoExporter<MessageInputUpload> { // hidden-source-line
} // hidden-source-line
}
Loading