import React, { useState, useRef } from 'react'; import { Textarea } from './ui/Textarea'; import { Button } from './ui/Button'; import { useAppStore } from '../store/useAppStore'; import { useImageGeneration, useImageEditing } from '../hooks/useImageGeneration'; import { Upload, Wand2, Edit3, MousePointer, HelpCircle, ChevronDown, ChevronRight, RotateCcw } from 'lucide-react'; import { blobToBase64, urlToBlob } from '../utils/imageUtils'; import { PromptHints } from './PromptHints'; import { PromptSuggestions } from './PromptSuggestions'; import { cn } from '../utils/cn'; export const PromptComposer: React.FC = () => { const { currentPrompt, setCurrentPrompt, selectedTool, setSelectedTool, temperature, setTemperature, seed, setSeed, isGenerating, uploadedImages, addUploadedImage, removeUploadedImage, clearUploadedImages, editReferenceImages, addEditReferenceImage, removeEditReferenceImage, clearEditReferenceImages, canvasImage, setCanvasImage, showPromptPanel, setShowPromptPanel, clearBrushStrokes, addBlob } = useAppStore(); const { generate, cancelGeneration } = useImageGeneration(); const { edit, cancelEdit } = useImageEditing(); const [showAdvanced, setShowAdvanced] = useState(false); const [showPromptSuggestions, setShowPromptSuggestions] = useState(true); const [showClearConfirm, setShowClearConfirm] = useState(false); const [showHintsModal, setShowHintsModal] = useState(false); const [isDragOver, setIsDragOver] = useState(false); const fileInputRef = useRef(null); const handleGenerate = async () => { if (!currentPrompt.trim()) return; if (selectedTool === 'generate') { // 将上传的图像转换为Blob对象 const referenceImageBlobs: Blob[] = []; for (const img of uploadedImages) { if (img.startsWith('data:')) { // 从base64数据创建Blob const base64 = img.split('base64,')[1]; const byteString = atob(base64); const mimeString = img.split(',')[0].split(':')[1].split(';')[0]; const ab = new ArrayBuffer(byteString.length); const ia = new Uint8Array(ab); for (let i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } referenceImageBlobs.push(new Blob([ab], { type: mimeString })); } else if (img.startsWith('blob:')) { // 从Blob URL获取Blob const { getBlob } = useAppStore.getState(); const blob = getBlob(img); if (blob) { referenceImageBlobs.push(blob); } else { // 如果在AppStore中找不到Blob,尝试重新创建 try { const response = await fetch(img); if (response.ok) { const blob = await response.blob(); // 重新添加到AppStore const newUrl = useAppStore.getState().addBlob(blob); referenceImageBlobs.push(blob); // 更新uploadedImages中的URL const index = uploadedImages.indexOf(img); if (index !== -1) { const newImages = [...uploadedImages]; newImages[index] = newUrl; useAppStore.getState().clearUploadedImages(); newImages.forEach(imageUrl => useAppStore.getState().addUploadedImage(imageUrl)); } } } catch (error) { console.warn('无法重新获取参考图像:', img, error); } } } else { // 从URL获取Blob try { const blob = await urlToBlob(img); referenceImageBlobs.push(blob); } catch (error) { console.warn('无法获取参考图像:', img, error); } } } generate({ prompt: currentPrompt, referenceImages: referenceImageBlobs.length > 0 ? referenceImageBlobs : undefined, temperature, seed: seed !== null ? seed : undefined }); } else if (selectedTool === 'edit' || selectedTool === 'mask') { edit(currentPrompt); } }; const handleFileUpload = async (file: File) => { if (file && file.type.startsWith('image/')) { try { // 直接使用Blob创建URL const blobUrl = addBlob(file); if (selectedTool === 'generate') { // 添加到参考图像(最多2张) if (uploadedImages.length < 2) { addUploadedImage(blobUrl); } } else if (selectedTool === 'edit') { // 编辑模式下,添加到单独的编辑参考图像(最多2张) if (editReferenceImages.length < 2) { addEditReferenceImage(blobUrl); } // 如果没有画布图像,则设置为画布图像 if (!canvasImage) { setCanvasImage(blobUrl); } } else if (selectedTool === 'mask') { // 遮罩模式下,立即设置为画布图像 clearUploadedImages(); addUploadedImage(blobUrl); setCanvasImage(blobUrl); } } catch (error) { console.error('上传图像失败:', error); } } }; const handleFileInputChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { await handleFileUpload(file); } }; const handleDragOver = (event: React.DragEvent) => { event.preventDefault(); setIsDragOver(true); }; const handleDragLeave = () => { setIsDragOver(false); }; const handleDrop = async (event: React.DragEvent) => { event.preventDefault(); setIsDragOver(false); const file = event.dataTransfer.files?.[0]; if (file) { await handleFileUpload(file); } }; const handleClearSession = () => { setCurrentPrompt(''); clearUploadedImages(); clearEditReferenceImages(); clearBrushStrokes(); setCanvasImage(null); setSeed(null); setTemperature(0.7); setShowClearConfirm(false); }; const tools = [ { id: 'generate', icon: Wand2, label: '生成', description: '从文本创建' }, { id: 'edit', icon: Edit3, label: '编辑', description: '修改现有图像' }, { id: 'mask', icon: MousePointer, label: '选择', description: '点击选择' }, ] as const; if (!showPromptPanel) { return (
); } return ( <>

模式

{tools.map((tool) => ( ))}
{/* 文件上传 */}
{selectedTool === 'mask' && (

使用遮罩编辑图像

)} {selectedTool === 'generate' && (

可选,最多2张图像

)} {selectedTool === 'edit' && (

{canvasImage ? '可选样式参考,最多2张图像' : '上传要编辑的图像,最多2张图像'}

)}

{isDragOver ? "释放文件以上传" : "拖拽或点击上传"}

支持 JPG, PNG, GIF

{/* Show uploaded images preview */} {((selectedTool === 'generate' && uploadedImages.length > 0) || (selectedTool === 'edit' && editReferenceImages.length > 0)) && (
{(selectedTool === 'generate' ? uploadedImages : editReferenceImages).map((image, index) => (
{`参考图像
参考 {index + 1}
))}
)}
{/* 提示输入 */}