From 640dbec2fb4a3d2ac3b3a249eb2075efa46afa95 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Fri, 24 Oct 2025 13:14:54 +0100 Subject: [PATCH] Add image clipboard support with paste detection and thumbnails --- src/components/prompt-input.tsx | 147 +++++++++++++++++++++----------- 1 file changed, 98 insertions(+), 49 deletions(-) diff --git a/src/components/prompt-input.tsx b/src/components/prompt-input.tsx index dbfa5456..779aafff 100644 --- a/src/components/prompt-input.tsx +++ b/src/components/prompt-input.tsx @@ -35,6 +35,7 @@ export default function PromptInput(props: PromptInputProps) { const [isDragging, setIsDragging] = createSignal(false) const [ignoredAtPositions, setIgnoredAtPositions] = createSignal>(new Set()) const [pasteCount, setPasteCount] = createSignal(0) + const [imageCount, setImageCount] = createSignal(0) let textareaRef: HTMLTextAreaElement | undefined let containerRef: HTMLDivElement | undefined @@ -74,7 +75,44 @@ export default function PromptInput(props: PromptInputProps) { } } - function handlePaste(e: ClipboardEvent) { + async function handlePaste(e: ClipboardEvent) { + const items = e.clipboardData?.items + if (!items) return + + for (let i = 0; i < items.length; i++) { + const item = items[i] + + if (item.type.startsWith("image/")) { + e.preventDefault() + + const blob = item.getAsFile() + if (!blob) continue + + const count = imageCount() + 1 + setImageCount(count) + + const reader = new FileReader() + reader.onload = () => { + const base64Data = (reader.result as string).split(",")[1] + const display = `[Image #${count}]` + const filename = `image-${count}.png` + + const attachment = createFileAttachment( + filename, + filename, + "image/png", + new TextEncoder().encode(base64Data), + props.instanceFolder, + ) + attachment.url = `data:image/png;base64,${base64Data}` + addAttachment(props.instanceId, props.sessionId, attachment) + } + reader.readAsDataURL(blob) + + return + } + } + const pastedText = e.clipboardData?.getData("text/plain") if (!pastedText) return @@ -307,6 +345,7 @@ export default function PromptInput(props: PromptInputProps) { clearAttachments(props.instanceId, props.sessionId) setIgnoredAtPositions(new Set()) setPasteCount(0) + setImageCount(0) if (textareaRef) { textareaRef.style.height = "auto" @@ -537,61 +576,71 @@ export default function PromptInput(props: PromptInputProps) { 0}>
- {(attachment) => ( -
- { + const isImage = attachment.mediaType.startsWith("image/") + return ( +
+ + + + } + > + + + + + } + > - } - > - - - - - } - > - - - - - {attachment.source.type === "text" ? attachment.display : attachment.filename} - -
- )} +
+ } + > + {attachment.filename} + + {attachment.source.type === "text" ? attachment.display : attachment.filename} + +
+ ) + }}