From ef559b2632b01e9763e9d657548ca5c4a3f6491a Mon Sep 17 00:00:00 2001 From: diamondplated Date: Sun, 29 Mar 2026 23:33:05 -0500 Subject: [PATCH] fix(app): prefer dropped files over file URIs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../components/prompt-input/attachments.ts | 19 ++++++------- .../src/components/prompt-input/drop.test.ts | 28 +++++++++++++++++++ .../app/src/components/prompt-input/drop.ts | 17 +++++++++++ 3 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 packages/app/src/components/prompt-input/drop.test.ts create mode 100644 packages/app/src/components/prompt-input/drop.ts diff --git a/packages/app/src/components/prompt-input/attachments.ts b/packages/app/src/components/prompt-input/attachments.ts index fa9930f6839a..1952d2387bcc 100644 --- a/packages/app/src/components/prompt-input/attachments.ts +++ b/packages/app/src/components/prompt-input/attachments.ts @@ -6,6 +6,7 @@ import { uuid } from "@/utils/uuid" import { getCursorPosition } from "./editor-dom" import { attachmentMime } from "./files" import { normalizePaste, pasteMode } from "./paste" +import { getDroppedPromptData } from "./drop" function dataUrl(file: File, mime: string) { return new Promise((resolve) => { @@ -165,19 +166,17 @@ export function createPromptAttachments(input: PromptAttachmentsInput) { event.preventDefault() input.setDraggingType(null) - const plainText = event.dataTransfer?.getData("text/plain") - const filePrefix = "file:" - if (plainText?.startsWith(filePrefix)) { - const filePath = plainText.slice(filePrefix.length) - input.focusEditor() - input.addPart({ type: "file", path: filePath, content: "@" + filePath, start: 0, end: 0 }) + const dropped = getDroppedPromptData(event.dataTransfer) + if (dropped.files.length > 0) { + await addAttachments(dropped.files) return } - const dropped = event.dataTransfer?.files - if (!dropped) return - - await addAttachments(Array.from(dropped)) + if (dropped.filePath) { + input.focusEditor() + input.addPart({ type: "file", path: dropped.filePath, content: "@" + dropped.filePath, start: 0, end: 0 }) + return + } } onMount(() => { diff --git a/packages/app/src/components/prompt-input/drop.test.ts b/packages/app/src/components/prompt-input/drop.test.ts new file mode 100644 index 000000000000..b82b86279cf9 --- /dev/null +++ b/packages/app/src/components/prompt-input/drop.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, test } from "bun:test" +import { getDroppedPromptData } from "./drop" + +describe("getDroppedPromptData", () => { + test("prefers dropped files over file uri text", () => { + const file = new File(["png"], "drop.png", { type: "image/png" }) + const dataTransfer = { + files: [file] as unknown as FileList, + getData: () => "file:/tmp/drop.png", + } + + const result = getDroppedPromptData(dataTransfer) + expect(result.files).toEqual([file]) + expect(result.filePath).toBeUndefined() + }) + + test("falls back to file uri text when no files are present", () => { + const dataTransfer = { + files: [] as unknown as FileList, + getData: () => "file:/tmp/drop.png", + } + + expect(getDroppedPromptData(dataTransfer)).toEqual({ + files: [], + filePath: "/tmp/drop.png", + }) + }) +}) diff --git a/packages/app/src/components/prompt-input/drop.ts b/packages/app/src/components/prompt-input/drop.ts new file mode 100644 index 000000000000..53f8c2482fbd --- /dev/null +++ b/packages/app/src/components/prompt-input/drop.ts @@ -0,0 +1,17 @@ +type DropDataTransfer = Pick + +export function getDroppedPromptData(dataTransfer: DropDataTransfer | null | undefined): { + files: File[] + filePath?: string +} { + const files = Array.from(dataTransfer?.files ?? []) + if (files.length > 0) return { files } + + const plainText = dataTransfer?.getData("text/plain") + const filePrefix = "file:" + if (plainText?.startsWith(filePrefix)) { + return { files: [], filePath: plainText.slice(filePrefix.length) } + } + + return { files: [] } +}