From 6bdd3528ac1b1824bd560678366ac82bb777db9c Mon Sep 17 00:00:00 2001 From: Devin Griffin <31415269+DNGriffin@users.noreply.github.com> Date: Sat, 7 Feb 2026 13:33:00 -0600 Subject: feat(app): drag-n-drop to @mention file (#12569) --- .../app/src/components/prompt-input/attachments.ts | 21 +++++++++++++++++---- .../src/components/prompt-input/drag-overlay.tsx | 6 +++--- 2 files changed, 20 insertions(+), 7 deletions(-) (limited to 'packages/app/src/components/prompt-input') diff --git a/packages/app/src/components/prompt-input/attachments.ts b/packages/app/src/components/prompt-input/attachments.ts index 4ea2cfb90..48eda3742 100644 --- a/packages/app/src/components/prompt-input/attachments.ts +++ b/packages/app/src/components/prompt-input/attachments.ts @@ -11,7 +11,8 @@ type PromptAttachmentsInput = { editor: () => HTMLDivElement | undefined isFocused: () => boolean isDialogActive: () => boolean - setDragging: (value: boolean) => void + setDraggingType: (type: "image" | "@mention" | null) => void + focusEditor: () => void addPart: (part: ContentPart) => void } @@ -84,15 +85,18 @@ export function createPromptAttachments(input: PromptAttachmentsInput) { event.preventDefault() const hasFiles = event.dataTransfer?.types.includes("Files") + const hasText = event.dataTransfer?.types.includes("text/plain") if (hasFiles) { - input.setDragging(true) + input.setDraggingType("image") + } else if (hasText) { + input.setDraggingType("@mention") } } const handleGlobalDragLeave = (event: DragEvent) => { if (input.isDialogActive()) return if (!event.relatedTarget) { - input.setDragging(false) + input.setDraggingType(null) } } @@ -100,7 +104,16 @@ export function createPromptAttachments(input: PromptAttachmentsInput) { if (input.isDialogActive()) return event.preventDefault() - input.setDragging(false) + 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 }) + return + } const dropped = event.dataTransfer?.files if (!dropped) return diff --git a/packages/app/src/components/prompt-input/drag-overlay.tsx b/packages/app/src/components/prompt-input/drag-overlay.tsx index f5a4d399e..e05b47d7c 100644 --- a/packages/app/src/components/prompt-input/drag-overlay.tsx +++ b/packages/app/src/components/prompt-input/drag-overlay.tsx @@ -2,16 +2,16 @@ import { Component, Show } from "solid-js" import { Icon } from "@opencode-ai/ui/icon" type PromptDragOverlayProps = { - dragging: boolean + type: "image" | "@mention" | null label: string } export const PromptDragOverlay: Component = (props) => { return ( - +
- + {props.label}
-- cgit v1.2.3