import { useState, useCallback } from 'react'; export interface PendingImage { base64: string; mediaType: string; preview: string; // data URL for display } export type ContentBlock = | { type: 'text'; text: string } | { type: 'image'; source: { type: 'base64'; media_type: string; data: string } }; /** * Hook for handling clipboard image paste in admin chat interfaces. * Manages pending images and builds multimodal content blocks for Claude API. */ export function useImagePaste() { const [pendingImages, setPendingImages] = useState([]); const handlePaste = useCallback((e: React.ClipboardEvent) => { const items = e.clipboardData?.items; if (!items) return; for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.type.startsWith('image/')) { e.preventDefault(); const file = item.getAsFile(); if (!file) continue; const reader = new FileReader(); reader.onload = () => { const dataUrl = reader.result as string; const base64 = dataUrl.split(',')[1]; const mediaType = file.type; setPendingImages(prev => [...prev, { base64, mediaType, preview: dataUrl }]); }; reader.readAsDataURL(file); break; // one image per paste } } }, []); const removePendingImage = useCallback((index: number) => { setPendingImages(prev => prev.filter((_, i) => i !== index)); }, []); const clearPendingImages = useCallback(() => { setPendingImages([]); }, []); /** * Build message content: plain string if text-only, array of blocks if images attached. */ const buildContent = useCallback((text: string): string | ContentBlock[] => { if (pendingImages.length === 0) return text; const blocks: ContentBlock[] = []; for (const img of pendingImages) { blocks.push({ type: 'image', source: { type: 'base64', media_type: img.mediaType, data: img.base64 }, }); } if (text) { blocks.push({ type: 'text', text }); } return blocks; }, [pendingImages]); return { pendingImages, handlePaste, removePendingImage, clearPendingImages, buildContent, }; }