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, Menu, ChevronDown, ChevronRight, RotateCcw } from 'lucide-react'; import { blobToBase64 } from '../utils/imageUtils'; import { PromptHints } from './PromptHints'; 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, } = useAppStore(); const { generate, cancelGeneration } = useImageGeneration(); const { edit, cancelEdit } = useImageEditing(); const [showAdvanced, setShowAdvanced] = useState(false); const [showClearConfirm, setShowClearConfirm] = useState(false); const [showHintsModal, setShowHintsModal] = useState(false); const [isDragOver, setIsDragOver] = useState(false); const fileInputRef = useRef(null); const handleGenerate = () => { if (!currentPrompt.trim()) return; if (selectedTool === 'generate') { const referenceImages = uploadedImages .filter(img => img.includes('base64,')) .map(img => img.split('base64,')[1]); generate({ prompt: currentPrompt, referenceImages: referenceImages.length > 0 ? referenceImages : undefined, temperature, seed: seed || undefined }); } else if (selectedTool === 'edit' || selectedTool === 'mask') { edit(currentPrompt); } }; const handleFileUpload = async (file: File) => { if (file && file.type.startsWith('image/')) { try { const base64 = await blobToBase64(file); const dataUrl = `data:${file.type};base64,${base64}`; if (selectedTool === 'generate') { // 添加到参考图像(最多2张) if (uploadedImages.length < 2) { addUploadedImage(dataUrl); } } else if (selectedTool === 'edit') { // 编辑模式下,添加到单独的编辑参考图像(最多2张) if (editReferenceImages.length < 2) { addEditReferenceImage(dataUrl); } // 如果没有画布图像,则设置为画布图像 if (!canvasImage) { setCanvasImage(dataUrl); } } else if (selectedTool === 'mask') { // 遮罩模式下,立即设置为画布图像 clearUploadedImages(); addUploadedImage(dataUrl); setCanvasImage(dataUrl); } } 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}
))}
)}
{/* 提示输入 */}