mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-27 15:13:37 +08:00
feat(desktop): auto-compress pasted/dropped images before upload
Images are now resized to max 1568px longest edge and re-encoded as JPEG (quality 0.85) before being attached. This prevents oversized base64 payloads hitting provider limits and fixes broken preview thumbnails caused by large objectURLs exhausting renderer memory. GIF images pass through unchanged (would lose animation). Compression applies to both paste and file-drop/select paths.
This commit is contained in:
parent
06156b445f
commit
476ab5da2d
@ -42,6 +42,7 @@ import {
|
||||
selectNativeFileAttachments,
|
||||
type ComposerAttachment,
|
||||
} from '../../lib/composerAttachments'
|
||||
import { compressDataUrl } from '../../lib/imageCompress'
|
||||
import { useComposerFileDrop } from './useComposerFileDrop'
|
||||
import { shouldSubmitOnEnter } from './sendShortcut'
|
||||
import {
|
||||
@ -903,17 +904,20 @@ export function ChatInput({ variant = 'default', compact = false }: ChatInputPro
|
||||
const id = `att-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
setComposerAttachments((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id,
|
||||
name: `pasted-image-${Date.now()}.png`,
|
||||
type: 'image',
|
||||
mimeType: file.type || 'image/png',
|
||||
previewUrl: reader.result as string,
|
||||
data: reader.result as string,
|
||||
},
|
||||
])
|
||||
const rawDataUrl = reader.result as string
|
||||
void compressDataUrl(rawDataUrl).then((compressed) => {
|
||||
setComposerAttachments((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id,
|
||||
name: `pasted-image-${Date.now()}.png`,
|
||||
type: 'image',
|
||||
mimeType: 'image/jpeg',
|
||||
previewUrl: compressed,
|
||||
data: compressed,
|
||||
},
|
||||
])
|
||||
})
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { getApiUrl } from '../api/client'
|
||||
import { isDesktopRuntime } from './desktopRuntime'
|
||||
import { getDesktopHost } from './desktopHost'
|
||||
import { compressDataUrl } from './imageCompress'
|
||||
|
||||
export type ComposerAttachment = {
|
||||
id: string
|
||||
@ -121,12 +122,13 @@ async function fileToComposerAttachment(file: File): Promise<ComposerAttachment
|
||||
}
|
||||
|
||||
const isImage = file.type.startsWith('image/')
|
||||
const data = await readFileAsDataUrl(file)
|
||||
const rawData = await readFileAsDataUrl(file)
|
||||
const data = isImage ? await compressDataUrl(rawData) : rawData
|
||||
return {
|
||||
id: nextAttachmentId(),
|
||||
name: file.name,
|
||||
type: isImage ? 'image' : 'file',
|
||||
mimeType: file.type || undefined,
|
||||
mimeType: isImage ? 'image/jpeg' : (file.type || undefined),
|
||||
previewUrl: isImage ? data : undefined,
|
||||
data,
|
||||
}
|
||||
|
||||
@ -5,13 +5,20 @@ export function planResize(w: number, h: number, maxEdge: number): { width: numb
|
||||
return { width: Math.round(w * scale), height: Math.round(h * scale) }
|
||||
}
|
||||
|
||||
/** 浏览器内:把 dataUrl 缩放到 maxEdge 内并以 quality 重新编码。 */
|
||||
export async function compressDataUrl(dataUrl: string, maxEdge = 1600, quality = 0.85): Promise<string> {
|
||||
/**
|
||||
* Compress a data:URL image by resizing to maxEdge and re-encoding as JPEG.
|
||||
* GIFs pass through unchanged (would lose animation).
|
||||
* Small images (≤ maxEdge both sides) still re-encode to reduce payload.
|
||||
*/
|
||||
export async function compressDataUrl(dataUrl: string, maxEdge = 1568, quality = 0.85): Promise<string> {
|
||||
if (!dataUrl.startsWith('data:')) return dataUrl
|
||||
if (dataUrl.startsWith('data:image/gif')) return dataUrl
|
||||
|
||||
const img = new Image()
|
||||
await new Promise<void>((res, rej) => { img.onload = () => res(); img.onerror = () => rej(new Error('load')); img.src = dataUrl })
|
||||
const { width, height } = planResize(img.naturalWidth, img.naturalHeight, maxEdge)
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = width; canvas.height = height
|
||||
canvas.getContext('2d')!.drawImage(img, 0, 0, width, height)
|
||||
return canvas.toDataURL('image/png', quality)
|
||||
return canvas.toDataURL('image/jpeg', quality)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user