fix: handle copy image from browser in linux
This commit is contained in:
parent
b915f1f674
commit
b93f77a9f5
@ -381,13 +381,53 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const clipboardItems = e.clipboardData?.items
|
const clipboardItems = e.clipboardData?.items
|
||||||
|
let hasProcessedImage = false
|
||||||
|
|
||||||
// Linux fallback: Use modern Clipboard API if clipboardData.items is unavailable
|
// Try clipboardData.items first (traditional method)
|
||||||
if (
|
if (clipboardItems && clipboardItems.length > 0) {
|
||||||
!clipboardItems &&
|
const imageItems = Array.from(clipboardItems).filter((item) =>
|
||||||
navigator.clipboard &&
|
item.type.startsWith('image/')
|
||||||
'read' in navigator.clipboard
|
)
|
||||||
) {
|
|
||||||
|
if (imageItems.length > 0) {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const files: File[] = []
|
||||||
|
let processedCount = 0
|
||||||
|
|
||||||
|
imageItems.forEach((item) => {
|
||||||
|
const file = item.getAsFile()
|
||||||
|
if (file) {
|
||||||
|
files.push(file)
|
||||||
|
}
|
||||||
|
processedCount++
|
||||||
|
|
||||||
|
// When all items are processed, handle the valid files
|
||||||
|
if (processedCount === imageItems.length) {
|
||||||
|
if (files.length > 0) {
|
||||||
|
const syntheticEvent = {
|
||||||
|
target: {
|
||||||
|
files: files,
|
||||||
|
},
|
||||||
|
} as unknown as React.ChangeEvent<HTMLInputElement>
|
||||||
|
|
||||||
|
handleFileChange(syntheticEvent)
|
||||||
|
hasProcessedImage = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// If we found image items but couldn't get files, fall through to modern API
|
||||||
|
if (processedCount === imageItems.length && !hasProcessedImage) {
|
||||||
|
// Continue to modern clipboard API fallback below
|
||||||
|
} else {
|
||||||
|
return // Successfully processed with traditional method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern Clipboard API fallback (for Linux, images copied from web, etc.)
|
||||||
|
if (navigator.clipboard && 'read' in navigator.clipboard) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -402,10 +442,11 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
for (const type of imageTypes) {
|
for (const type of imageTypes) {
|
||||||
try {
|
try {
|
||||||
const blob = await item.getType(type)
|
const blob = await item.getType(type)
|
||||||
// Convert blob to File
|
// Convert blob to File with better naming
|
||||||
|
const extension = type.split('/')[1] || 'png'
|
||||||
const file = new File(
|
const file = new File(
|
||||||
[blob],
|
[blob],
|
||||||
`pasted-image.${type.split('/')[1]}`,
|
`pasted-image-${Date.now()}.${extension}`,
|
||||||
{ type }
|
{ type }
|
||||||
)
|
)
|
||||||
files.push(file)
|
files.push(file)
|
||||||
@ -423,47 +464,16 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
} as unknown as React.ChangeEvent<HTMLInputElement>
|
} as unknown as React.ChangeEvent<HTMLInputElement>
|
||||||
|
|
||||||
handleFileChange(syntheticEvent)
|
handleFileChange(syntheticEvent)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error reading clipboard contents:', error)
|
console.error('Clipboard API access failed:', error)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Original logic for browsers with working clipboardData.items
|
// If we reach here, no image was found or processed
|
||||||
if (!clipboardItems) {
|
if (!hasProcessedImage) {
|
||||||
return
|
console.log('No image data found in clipboard or clipboard access failed')
|
||||||
}
|
|
||||||
|
|
||||||
const imageItems = Array.from(clipboardItems).filter((item) =>
|
|
||||||
item.type.startsWith('image/')
|
|
||||||
)
|
|
||||||
|
|
||||||
if (imageItems.length > 0) {
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
const files: File[] = []
|
|
||||||
let processedCount = 0
|
|
||||||
|
|
||||||
imageItems.forEach((item) => {
|
|
||||||
const file = item.getAsFile()
|
|
||||||
if (file) {
|
|
||||||
files.push(file)
|
|
||||||
}
|
|
||||||
processedCount++
|
|
||||||
|
|
||||||
// When all items are processed, handle the valid files
|
|
||||||
if (processedCount === imageItems.length && files.length > 0) {
|
|
||||||
const syntheticEvent = {
|
|
||||||
target: {
|
|
||||||
files: files,
|
|
||||||
},
|
|
||||||
} as unknown as React.ChangeEvent<HTMLInputElement>
|
|
||||||
|
|
||||||
handleFileChange(syntheticEvent)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user