import { IS_WORDPRESS, PLUGIN_REST_ENDPOINTS, getRestNonce } from '../constants'; export type ChatAttachmentSource = 'user_upload' | 'site_media' | 'rendered_screenshot'; export type ChatAttachmentDetail = 'auto' | 'low' | 'high'; export interface ChatAttachmentInput { kind: 'image'; source: ChatAttachmentSource; attachment_id?: number; name: string; mime: string; url?: string; width?: number; height?: number; detail: ChatAttachmentDetail; data_url: string; } export interface UploadedImageAttachment { attachmentId?: number; name: string; mime: string; url?: string; width?: number; height?: number; dataUrl: string; } const SUPPORTED_IMAGE_MIME_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp', 'image/gif']); export function isSupportedImageFile(file: File): boolean { return SUPPORTED_IMAGE_MIME_TYPES.has(file.type); } function readFileAsDataUrl(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { if (typeof reader.result === 'string') resolve(reader.result); else reject(new Error('FileReader did not return a data URL')); }; reader.onerror = () => reject(reader.error ?? new Error('Failed to read image file')); reader.readAsDataURL(file); }); } async function uploadToWordPressMediaLibrary(file: File): Promise> { const body = new FormData(); body.append('file', file); const response = await fetch(PLUGIN_REST_ENDPOINTS.attachments, { method: 'POST', credentials: 'include', headers: { 'X-WP-Nonce': getRestNonce() }, body, }); if (!response.ok) { throw new Error('Failed to upload image attachment'); } const payload = await response.json(); return { attachmentId: typeof payload.attachment_id === 'number' ? payload.attachment_id : undefined, name: typeof payload.name === 'string' && payload.name.trim() ? payload.name : file.name, mime: typeof payload.mime === 'string' && payload.mime ? payload.mime : file.type, url: typeof payload.url === 'string' ? payload.url : undefined, width: typeof payload.width === 'number' ? payload.width : undefined, height: typeof payload.height === 'number' ? payload.height : undefined, }; } export async function uploadImageAttachment(file: File): Promise { if (!isSupportedImageFile(file)) { throw new Error('Unsupported image type'); } const dataUrl = await readFileAsDataUrl(file); if (!IS_WORDPRESS) { return { name: file.name, mime: file.type, dataUrl, }; } const media = await uploadToWordPressMediaLibrary(file); return { ...media, dataUrl }; } export function composerAttachmentToChatInput(attachment: { attachmentId?: number; name: string; mime: string; url?: string; width?: number; height?: number; dataUrl?: string; }): ChatAttachmentInput | null { if (!attachment.dataUrl) return null; return { kind: 'image', source: 'user_upload', attachment_id: attachment.attachmentId, name: attachment.name, mime: attachment.mime, url: attachment.url, width: attachment.width, height: attachment.height, detail: 'auto', data_url: attachment.dataUrl, }; }