Spaces:
Sleeping
Sleeping
| import { useState } from 'react'; | |
| import { Image as ImageIcon, Loader2, Sparkles, Download } from 'lucide-react'; | |
| export default function ComfyUI() { | |
| const [prompt, setPrompt] = useState(''); | |
| const [loading, setLoading] = useState(false); | |
| const [imageUrl, setImageUrl] = useState(''); | |
| const [error, setError] = useState(''); | |
| const handleGenerate = async () => { | |
| if (!prompt.trim()) return; | |
| setLoading(true); | |
| setError(''); | |
| setImageUrl(''); | |
| try { | |
| const response = await fetch('/api/comfy', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ prompt }), | |
| }); | |
| if (!response.ok) throw new Error('Failed to generate image'); | |
| const data = await response.json(); | |
| setImageUrl(data.url); | |
| } catch (err) { | |
| setError(err.message || 'Something went wrong'); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div className="bg-slate-800 rounded-xl p-6 shadow-xl border border-slate-700"> | |
| <div className="flex items-center justify-between mb-4"> | |
| <div className="flex items-center gap-2"> | |
| <ImageIcon className="w-5 h-5 text-pink-500" /> | |
| <h2 className="text-lg font-semibold text-white">ComfyUI Image Gen</h2> | |
| </div> | |
| </div> | |
| <div className="space-y-4"> | |
| <div> | |
| <label className="block text-sm font-medium text-slate-400 mb-1"> | |
| Prompt | |
| </label> | |
| <textarea | |
| value={prompt} | |
| onChange={(e) => setPrompt(e.target.value)} | |
| placeholder="A futuristic cyberpunk city with neon lights..." | |
| className="w-full bg-slate-900 border border-slate-600 rounded-lg p-3 text-white placeholder-slate-500 focus:ring-2 focus:ring-pink-500 focus:border-transparent outline-none transition-all resize-none h-24" | |
| /> | |
| </div> | |
| <button | |
| onClick={handleGenerate} | |
| disabled={loading} | |
| className="w-full bg-gradient-to-r from-pink-600 to-purple-600 hover:from-pink-500 hover:to-purple-500 text-white font-medium py-2.5 rounded-lg transition-all flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg shadow-pink-900/20" | |
| > | |
| {loading ? ( | |
| <> | |
| <Loader2 className="w-4 h-4 animate-spin" /> | |
| Generating... | |
| </> | |
| ) : ( | |
| <> | |
| <Sparkles className="w-4 h-4" /> | |
| Generate Image | |
| </> | |
| )} | |
| </button> | |
| {error && ( | |
| <div className="bg-red-900/30 text-red-400 p-3 rounded-lg text-sm border border-red-800 flex items-center gap-2"> | |
| <span>⚠️ {error}</span> | |
| </div> | |
| )} | |
| {imageUrl && ( | |
| <div className="mt-4 border border-slate-600 rounded-lg overflow-hidden bg-slate-900 relative group"> | |
| <img src={imageUrl} alt="Generated content" className="w-full h-auto object-cover min-h-[200px]" /> | |
| <a | |
| href={imageUrl} | |
| download="generated-image.jpg" | |
| target="_blank" | |
| className="absolute top-2 right-2 bg-black/50 hover:bg-black/70 text-white p-2 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity" | |
| title="Download Image" | |
| > | |
| <Download className="w-4 h-4" /> | |
| </a> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| } |