Improve pasted text behaviour in Multimodaltextbox (#10135)

* handle pasted text as file

* test

* add changeset

* remove unneeded test

* add max_plain_text_length param

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
This commit is contained in:
Hannah 2024-12-09 21:37:29 +00:00 committed by GitHub
parent 7ca36850c9
commit 3e93740f05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 56 additions and 1 deletions

View File

@ -0,0 +1,6 @@
---
"@gradio/multimodaltextbox": minor
"gradio": minor
---
feat:Improve pasted text behaviour in `Multimodaltextbox`

View File

@ -86,6 +86,7 @@ class MultimodalTextbox(FormComponent):
rtl: bool = False,
submit_btn: str | bool | None = True,
stop_btn: str | bool | None = False,
max_plain_text_length: int = 1000,
):
"""
Parameters:
@ -115,6 +116,7 @@ class MultimodalTextbox(FormComponent):
autoscroll: If True, will automatically scroll to the bottom of the textbox when the value changes, unless the user scrolls up. If False, will not scroll to the bottom of the textbox when the value changes.
submit_btn: If False, will not show a submit button. If a string, will use that string as the submit button text.
stop_btn: If True, will show a stop button (useful for streaming demos). If a string, will use that string as the stop button text.
max_plain_text_length: Maximum length of plain text in the textbox. If the text exceeds this length, the text will be pasted as a file. Default is 1000.
"""
self.file_types = file_types
self.file_count = file_count
@ -129,6 +131,7 @@ class MultimodalTextbox(FormComponent):
self.stop_btn = stop_btn
self.autofocus = autofocus
self.autoscroll = autoscroll
self.max_plain_text_length = max_plain_text_length
super().__init__(
label=label,

View File

@ -52,6 +52,7 @@
export let interactive: boolean;
export let root: string;
export let file_count: "single" | "multiple" | "directory";
export let max_plain_text_length: number;
let dragging: boolean;
</script>
@ -109,5 +110,6 @@
disabled={!interactive}
upload={(...args) => gradio.client.upload(...args)}
stream_handler={(...args) => gradio.client.stream(...args)}
{max_plain_text_length}
/>
</Block>

View File

@ -47,6 +47,7 @@
export let upload: Client["upload"];
export let stream_handler: Client["stream"];
export let file_count: "single" | "multiple" | "directory" = "multiple";
export let max_plain_text_length = 1000;
let upload_component: Upload;
let hidden_upload: HTMLInputElement;
@ -194,9 +195,23 @@
dispatch("submit");
}
function handle_paste(event: ClipboardEvent): void {
async function handle_paste(event: ClipboardEvent): Promise<void> {
if (!event.clipboardData) return;
const items = event.clipboardData.items;
const text = event.clipboardData.getData("text");
if (text && text.length > max_plain_text_length) {
event.preventDefault();
const file = new window.File([text], "pasted_text.txt", {
type: "text/plain",
lastModified: Date.now()
});
if (upload_component) {
upload_component.load_files([file]);
}
return;
}
for (let index in items) {
const item = items[index];
if (item.kind === "file" && item.type.includes("image")) {

View File

@ -266,4 +266,33 @@ for (const msg_format of ["tuples", "messages"]) {
await expect(page.locator(".thumbnail-image")).toHaveCount(2);
});
test(`message format ${msg_format} - pasting large text should create a file upload`, async ({
page
}) => {
if (msg_format === "tuples") {
await go_to_testcase(page, "tuples");
}
const textbox = await page.getByTestId("textbox");
const largeText = "x".repeat(2000);
await textbox.focus();
await page.evaluate((text) => {
const dataTransfer = new DataTransfer();
const clipboardData = new ClipboardEvent("paste", {
clipboardData: dataTransfer,
bubbles: true,
cancelable: true
});
dataTransfer.setData("text/plain", text);
document.activeElement?.dispatchEvent(clipboardData);
}, largeText);
await expect(page.locator(".thumbnail-item")).toBeVisible();
const fileIcon = await page.locator(".thumbnail-item").first();
await expect(fileIcon).toBeVisible();
const textboxValue = await textbox.inputValue();
await expect(textboxValue).toBe("");
});
}