









@@ -453,6 +453,32 @@ function chatAttachmentFromFile(file: File, dataUrl: string): ChatAttachment {
453453return registerChatAttachmentPayload({ attachment, dataUrl, file });
454454}
455455456+function dataImageClipboardFile(dataUrl: string): { file: File; dataUrl: string } | null {
457+const match = /^\s*data:(image\/[a-z0-9.+-]+);base64,([a-z0-9+/=\s]+)\s*$/i.exec(dataUrl);
458+if (!match) {
459+return null;
460+}
461+const mimeType = match[1].toLowerCase();
462+if (!isSupportedChatAttachmentFile({ name: "pasted-image", type: mimeType })) {
463+return null;
464+}
465+const base64 = match[2].replace(/\s+/g, "");
466+try {
467+const binary = atob(base64);
468+const bytes = new Uint8Array(binary.length);
469+for (let i = 0; i < binary.length; i++) {
470+bytes[i] = binary.charCodeAt(i);
471+}
472+const extension = mimeType.split("/")[1]?.replace(/[^a-z0-9.+-]/gi, "") || "png";
473+return {
474+file: new File([bytes], `pasted-image.${extension}`, { type: mimeType }),
475+dataUrl: `data:${mimeType};base64,${base64}`,
476+};
477+} catch {
478+return null;
479+}
480+}
481+456482function isImageAttachment(att: ChatAttachment): boolean {
457483return att.mimeType.startsWith("image/");
458484}
@@ -470,6 +496,16 @@ function handlePaste(e: ClipboardEvent, props: ChatProps) {
470496}
471497}
472498if (imageItems.length === 0) {
499+const text = e.clipboardData?.getData("text/plain");
500+const pasted = text ? dataImageClipboardFile(text) : null;
501+if (!pasted) {
502+return;
503+}
504+e.preventDefault();
505+props.onAttachmentsChange([
506+ ...(props.attachments ?? []),
507+chatAttachmentFromFile(pasted.file, pasted.dataUrl),
508+]);
473509return;
474510}
475511e.preventDefault();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。