From 14c590db0604a34c108b4b24748bf9ef228a2730 Mon Sep 17 00:00:00 2001 From: HardMax71 Date: Wed, 7 Oct 2026 17:34:16 +0200 Subject: [PATCH] fix(prompt-input): paste a spreadsheet selection as text, not an image --- .../prompt-input-paste-text-with-image.md | 5 + .../components/(chatbot)/prompt-input.mdx | 1 + .../elements/__tests__/prompt-input.test.tsx | 91 +++++++++++++++++++ packages/elements/src/prompt-input.tsx | 8 ++ 4 files changed, 105 insertions(+) create mode 100644 .changeset/prompt-input-paste-text-with-image.md diff --git a/.changeset/prompt-input-paste-text-with-image.md b/.changeset/prompt-input-paste-text-with-image.md new file mode 100644 index 00000000..c5e66d8c --- /dev/null +++ b/.changeset/prompt-input-paste-text-with-image.md @@ -0,0 +1,5 @@ +--- +"ai-elements": patch +--- + +Paste the text of a spreadsheet or document selection in PromptInput instead of attaching its image diff --git a/apps/docs/content/components/(chatbot)/prompt-input.mdx b/apps/docs/content/components/(chatbot)/prompt-input.mdx index ad2c9f5b..11950e4d 100644 --- a/apps/docs/content/components/(chatbot)/prompt-input.mdx +++ b/apps/docs/content/components/(chatbot)/prompt-input.mdx @@ -241,6 +241,7 @@ export async function POST(req: Request) { - Auto-resizing textarea that adjusts height based on content - File attachment support with drag-and-drop +- Paste files from the clipboard; a spreadsheet or document selection pastes as text - Built-in screenshot capture action - Image preview for image attachments - Configurable file constraints (max files, max size, accepted types) diff --git a/packages/elements/__tests__/prompt-input.test.tsx b/packages/elements/__tests__/prompt-input.test.tsx index d961a3ba..7927892e 100644 --- a/packages/elements/__tests__/prompt-input.test.tsx +++ b/packages/elements/__tests__/prompt-input.test.tsx @@ -1997,6 +1997,7 @@ describe("paste functionality", () => { // Mock clipboardData items pasteEvent.clipboardData = { + getData: () => "", items: [ { getAsFile: () => file, @@ -2014,6 +2015,96 @@ describe("paste functionality", () => { }); }); + it("pastes the text of a spreadsheet selection instead of its image", async () => { + setupPromptInputTests(); + const onSubmit = vi.fn(); + + const AttachmentConsumer = () => { + const attachments = usePromptInputAttachments(); + return
{attachments.files.length}
; + }; + + render( + + + + + + + ); + + const textarea = screen.getByPlaceholderText( + "What would you like to know?" + ); + textarea.focus(); + + const clipboardData = new DataTransfer(); + clipboardData.setData("text/plain", "Q1\t42"); + clipboardData.setData( + "text/html", + "
Q142
" + ); + clipboardData.items.add( + new File(["image"], "image.png", { type: "image/png" }) + ); + const pasteEvent = new ClipboardEvent("paste", { + bubbles: true, + cancelable: true, + clipboardData, + }); + + await act(() => { + textarea.dispatchEvent(pasteEvent); + }); + + expect(pasteEvent.defaultPrevented).toBeFalsy(); + expect(screen.getByTestId("count")).toHaveTextContent("0"); + }); + + it("attaches a copied file whose name is also on the clipboard as text", async () => { + setupPromptInputTests(); + const onSubmit = vi.fn(); + + const AttachmentConsumer = () => { + const attachments = usePromptInputAttachments(); + return
{attachments.files.length}
; + }; + + render( + + + + + + + ); + + const textarea = screen.getByPlaceholderText( + "What would you like to know?" + ); + textarea.focus(); + + const clipboardData = new DataTransfer(); + clipboardData.setData("text/plain", "report.png"); + clipboardData.items.add( + new File(["image"], "report.png", { type: "image/png" }) + ); + const pasteEvent = new ClipboardEvent("paste", { + bubbles: true, + cancelable: true, + clipboardData, + }); + + await act(() => { + textarea.dispatchEvent(pasteEvent); + }); + + await vi.waitFor(() => { + expect(screen.getByTestId("count")).toHaveTextContent("1"); + }); + expect(pasteEvent.defaultPrevented).toBeTruthy(); + }); + it("handles paste with no files", () => { setupPromptInputTests(); const onSubmit = vi.fn(); diff --git a/packages/elements/src/prompt-input.tsx b/packages/elements/src/prompt-input.tsx index 412c846d..3deb37c6 100644 --- a/packages/elements/src/prompt-input.tsx +++ b/packages/elements/src/prompt-input.tsx @@ -1019,6 +1019,14 @@ export const PromptInputTextarea = ({ return; } + // Copied spreadsheet cells also carry a picture of the selection; paste their text instead + if ( + event.clipboardData.getData("text/plain") && + event.clipboardData.getData("text/html") + ) { + return; + } + const files: File[] = []; for (const item of items) {