Skip to content

feat: support pasting images from clipboard in chat input#93

Draft
MrOrz wants to merge 2 commits into
masterfrom
claude/friendly-mayer-40aw4j
Draft

feat: support pasting images from clipboard in chat input#93
MrOrz wants to merge 2 commits into
masterfrom
claude/friendly-mayer-40aw4j

Conversation

@MrOrz

@MrOrz MrOrz commented Jun 16, 2026

Copy link
Copy Markdown
Member

$(cat <<'EOF'

Summary

  • Adds onPaste handler to the chat textarea in ChatInput.tsx
  • Detects image files in clipboard data (clipboardData.items) and appends them as File objects
  • Pasted images are named "Pasted image 1", "Pasted image 2", etc.
  • Pasted images flow through the exact same upload pipeline as file-picker attachments (base64 → SaveFilesAsArtifactsPlugin → artifact store)

Test plan

  • Focus the textarea, copy an image to clipboard, paste (Ctrl+V / ⌘V) → pill "Pasted image 1" appears
  • Paste again → "Pasted image 2" appears
  • Send the message → image is visible as inline preview in the user bubble
  • File picker upload still works unchanged
  • Pasting plain text still works normally (no extra pills)

https://claude.ai/code/session_016m1gpCeYMUSPFCJygVn2aj
EOF
)


Generated by Claude Code

Adds an onPaste handler to the textarea that detects image files in
clipboard data and appends them as File objects named "Pasted image #".
Pasted images flow through the same upload pipeline as file-picker
attachments.

https://claude.ai/code/session_016m1gpCeYMUSPFCJygVn2aj

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for pasting images directly into the ChatInput component's textarea. The reviewer identified three critical issues with the paste handler: a potential runtime crash from using a non-null assertion on item.getAsFile(), missing file extensions on the created files, and the lack of e.preventDefault() which can trigger unwanted default browser paste behavior. A refactored code snippet was suggested to address these concerns.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +69 to +82
(e: React.ClipboardEvent<HTMLTextAreaElement>) => {
const imageFiles = Array.from(e.clipboardData.items)
.filter((item) => item.kind === 'file' && item.type.startsWith('image/'))
.map((item) => {
const file = item.getAsFile()!
pasteCounter.current += 1
return new File([file], `Pasted image ${pasteCounter.current}`, {
type: file.type,
})
})
if (imageFiles.length > 0) {
setFiles((prev) => [...prev, ...imageFiles])
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There are three issues with the current paste handler implementation:

  1. Potential Null Pointer / Crash: item.getAsFile() can return null. Using the non-null assertion operator (!) is unsafe and can lead to runtime errors if the file cannot be retrieved.
  2. Missing File Extension: Naming the file Pasted image X without an extension (e.g., .png or .jpg) can cause issues with backend upload pipelines or APIs that rely on file extensions to determine the file type.
  3. Unwanted Default Paste Behavior: If e.preventDefault() is not called when an image is pasted, the browser may also trigger its default paste behavior, which can insert unwanted text/HTML representations of the image into the textarea.

We can resolve these issues by safely retrieving the file, extracting the extension from the MIME type, and calling e.preventDefault() when images are successfully processed.

    (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
      const imageFiles: File[] = []
      for (const item of Array.from(e.clipboardData.items)) {
        if (item.kind === 'file' && item.type.startsWith('image/')) {
          const file = item.getAsFile()
          if (file) {
            pasteCounter.current += 1
            const ext = file.type.split('/')[1] || 'png'
            imageFiles.push(
              new File([file], `Pasted image ${pasteCounter.current}.${ext}`, {
                type: file.type,
              })
            )
          }
        }
      }
      if (imageFiles.length > 0) {
        setFiles((prev) => [...prev, ...imageFiles])
        e.preventDefault()
      }
    },

…aste

- Replace non-null assertion with null guard using flatMap/for loop
- Derive file extension from MIME type (e.g. image/png → .png)
- Call e.preventDefault() only when images are consumed to prevent
  browsers from inserting garbage text representations into the textarea

https://claude.ai/code/session_016m1gpCeYMUSPFCJygVn2aj
Base automatically changed from claude/sweet-volta-q4zgrp to master June 17, 2026 10:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants