79 lines
2.2 KiB
TypeScript
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,
|
|
};
|
|
}
|