✨ feat(vscode): 问 AI 输入框能粘贴、拖进图片随问题发给 Claude Code,缩略图能点开看大图,图片存进 .spx/code-qa 跟着问答删
This commit is contained in:
1 parent
d9a5c3673e
commit
36d842e18e
28 files changed
+825
-121
No files matched your search
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 「问 AI」输入框里贴图、拖图的规则(不碰 React):认哪些格式、每张多大、最多几张,
|
||||
* 贴进来还没发的图片在页面上的样子,发给扩展端的样子。
|
||||
*/
|
||||
|
||||
import type { AskImage } from './types'
|
||||
|
||||
/** 认的格式:Claude 认的这几种 */
|
||||
export const IMAGE_TYPES: readonly string[] = ['image/png', 'image/jpeg', 'image/gif', 'image/webp']
|
||||
|
||||
export const MAX_IMAGE_BYTES = 5 * 1024 * 1024
|
||||
|
||||
/** 一个问题最多带几张 */
|
||||
export const MAX_IMAGES = 6
|
||||
|
||||
/** 贴进输入框、还没发的一张图片:url 是 data: 地址,缩略图、大图、发送都用它 */
|
||||
export interface PendingImage {
|
||||
id: number
|
||||
mediaType: string
|
||||
url: string
|
||||
}
|
||||
|
||||
/** 剪贴板 / 拖进来的东西里的文件;文字、网页片段这些不算(照常粘成文字) */
|
||||
export function transferFiles<F>(items: ArrayLike<{ kind: string, getAsFile: () => F | null }>): F[] {
|
||||
return Array.from(items).flatMap(item => item.kind === 'file' ? item.getAsFile() ?? [] : [])
|
||||
}
|
||||
|
||||
/**
|
||||
* 挑出能带上的:只认 PNG、JPEG、GIF、WebP,每张不超过 MAX_IMAGE_BYTES,连同已经有的 have 张最多 MAX_IMAGES 张,按先后取;
|
||||
* 有没加上的给一句原因。
|
||||
*/
|
||||
export function pickImages<F extends { type: string, size: number }>(files: readonly F[], have: number): { accepted: F[], notice?: string } {
|
||||
const images = files.filter(file => IMAGE_TYPES.includes(file.type))
|
||||
const small = images.filter(file => file.size <= MAX_IMAGE_BYTES)
|
||||
const accepted = small.slice(0, Math.max(0, MAX_IMAGES - have))
|
||||
const reasons = [
|
||||
...(images.length < files.length ? ['只能贴 PNG、JPEG、GIF、WebP 图片'] : []),
|
||||
...(small.length < images.length ? [`每张图片不能超过 ${MAX_IMAGE_BYTES / 1024 / 1024} MB`] : []),
|
||||
...(accepted.length < small.length ? [`一个问题最多带 ${MAX_IMAGES} 张图片`] : []),
|
||||
]
|
||||
return reasons.length === 0
|
||||
? { accepted }
|
||||
: { accepted, notice: `${reasons.join(';')},有 ${files.length - accepted.length} 个没加上。` }
|
||||
}
|
||||
|
||||
let imageCounter = 0
|
||||
|
||||
/** 读成 data: 地址(缩略图直接用,页面的安全策略允许 data: 图片) */
|
||||
export function readImage(file: File): Promise<PendingImage> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => resolve({ id: ++imageCounter, mediaType: file.type, url: String(reader.result) })
|
||||
reader.onerror = () => reject(reader.error ?? new Error('读不出图片'))
|
||||
reader.readAsDataURL(file)
|
||||
})
|
||||
}
|
||||
|
||||
/** 发给扩展端的样子:data: 地址去掉前缀剩下的 base64 */
|
||||
export function toAskImage(image: PendingImage): AskImage {
|
||||
return { mediaType: image.mediaType, base64: image.url.slice(image.url.indexOf(',') + 1) }
|
||||
}
|
||||
Reference in new issue
Block a user