iconsulting/packages/admin-client/src/shared/hooks/useImagePaste.ts

79 lines
2.2 KiB
TypeScript

import { useState, useCallback } from 'react';
export interface PendingImage {
base64: string;
mediaType: string;
preview: string; // data URL for <img> 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<PendingImage[]>([]);
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,
};
}