ummat epaper

import { useState, useRef, useCallback } from "react"; const SLOT_CONFIG = [ { key: "subject", label: "Subject", icon: "◈", hint: "Person, object, character", color: "#E8C547", }, { key: "scene", label: "Scene", icon: "◉", hint: "Background, environment", color: "#47C5E8", }, { key: "style", label: "Style", icon: "◎", hint: "Art style, aesthetic", color: "#E847A3", }, ]; function ImageSlot({ config, image, onUpload, onClear }) { const inputRef = useRef(); const [dragging, setDragging] = useState(false); const handleFile = (file) => { if (!file || !file.type.startsWith("image/")) return; const reader = new FileReader(); reader.onload = (e) => onUpload({ dataUrl: e.target.result, file }); reader.readAsDataURL(file); }; return (
{config.icon} {config.label}
!image && inputRef.current.click()} onDragOver={(e) => { e.preventDefault(); setDragging(true); }} onDragLeave={() => setDragging(false)} onDrop={(e) => { e.preventDefault(); setDragging(false); handleFile(e.dataTransfer.files[0]); }} style={{ width: "100%", aspectRatio: "1", borderRadius: 16, border: `2px dashed ${dragging ? config.color : image ? "transparent" : "#333"}`, background: image ? "transparent" : "#111", cursor: image ? "default" : "pointer", position: "relative", overflow: "hidden", transition: "all 0.2s", boxShadow: image ? `0 0 30px ${config.color}22` : "none", }} > {image ? ( <> {config.label}
e.currentTarget.style.background = "rgba(0,0,0,0.4)"} onMouseLeave={(e) => e.currentTarget.style.background = "rgba(0,0,0,0)"} >
) : (
+
{config.hint}
)} handleFile(e.target.files[0])} />
); } async function analyzeImageWithClaude(dataUrl, role) { const base64 = dataUrl.split(",")[1]; const mediaType = dataUrl.split(";")[0].split(":")[1]; const res = await fetch("https://api.anthropic.com/v1/messages", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: "claude-sonnet-4-20250514", max_tokens: 1000, messages: [{ role: "user", content: [ { type: "image", source: { type: "base64", media_type: mediaType, data: base64 } }, { type: "text", text: role === "subject" ? "Describe the main subject of this image concisely for an AI image generator. Focus on: what/who it is, key visual features, pose, colors. Be specific and descriptive. 2-3 sentences max." : role === "scene" ? "Describe the setting/environment/background of this image concisely for an AI image generator. Focus on: location, lighting, atmosphere, time of day, surroundings. 2-3 sentences max." : "Describe the artistic style of this image concisely for an AI image generator. Focus on: art style, medium, color palette, mood, technique, rendering style. 2-3 sentences max." } ] }] }) }); const data = await res.json(); return data.content[0].text; } export default function WhiskClone() { const [images, setImages] = useState({ subject: null, scene: null, style: null }); const [extraPrompt, setExtraPrompt] = useState(""); const [status, setStatus] = useState(""); const [result, setResult] = useState(null); const [loading, setLoading] = useState(false); const [descriptions, setDescriptions] = useState({}); const hasAny = images.subject || images.scene || images.style; const handleGenerate = async () => { if (!hasAny) return; setLoading(true); setResult(null); setDescriptions({}); try { const descs = {}; for (const slot of SLOT_CONFIG) { if (images[slot.key]) { setStatus(`Analyzing ${slot.label}...`); descs[slot.key] = await analyzeImageWithClaude(images[slot.key].dataUrl, slot.key); setDescriptions(d => ({ ...d, [slot.key]: descs[slot.key] })); } } setStatus("Generating image..."); const parts = []; if (descs.subject) parts.push(descs.subject); if (descs.scene) parts.push(`Set in: ${descs.scene}`); if (descs.style) parts.push(`Rendered in the style of: ${descs.style}`); if (extraPrompt.trim()) parts.push(extraPrompt.trim()); const finalPrompt = parts.join(". "); const seed = Math.floor(Math.random() * 99999); const url = `https://image.pollinations.ai/prompt/${encodeURIComponent(finalPrompt)}?width=768&height=768&seed=${seed}&nologo=true`; setResult({ url, prompt: finalPrompt }); setStatus("Done!"); } catch (err) { setStatus("Error: " + err.message); } setLoading(false); }; return (
{/* Header */}
Whisk
IMAGE REMIX STUDIO
{/* Left Panel */}
Upload images for subject, scene, and style — AI will blend them into something new.
{/* Image Slots */}
{SLOT_CONFIG.map(cfg => ( setImages(prev => ({ ...prev, [cfg.key]: img }))} onClear={() => setImages(prev => ({ ...prev, [cfg.key]: null }))} /> ))}
{/* Extra prompt */}
setExtraPrompt(e.target.value)} placeholder='e.g. "at sunset" or "make it look magical"' style={{ width: "100%", background: "#111", border: "1px solid #222", borderRadius: 10, padding: "11px 14px", color: "#fff", fontSize: 13, outline: "none", boxSizing: "border-box", fontFamily: "inherit", }} />
{/* Generate Button */} {/* Descriptions */} {Object.keys(descriptions).length > 0 && (
{SLOT_CONFIG.map(cfg => descriptions[cfg.key] && (
{cfg.label}
{descriptions[cfg.key]}
))}
)}
{/* Right Panel - Result */}
{loading ? (
{status}
) : result ? (
Generated e.target.style.opacity = 1} style={{ width: "100%", borderRadius: 20, opacity: 0, transition: "opacity 0.6s", boxShadow: "0 0 80px rgba(232, 197, 71, 0.1)", }} />
href={result.url} download="whisk-remix.jpg" target="_blank" rel="noreferrer" style={{ padding: "10px 24px", background: "#fff", color: "#000", borderRadius: 8, textDecoration: "none", fontSize: 13, fontWeight: 600, }} >↓ Download
VIEW PROMPT
{result.prompt}
) : (
Upload images to remix
Subject + Scene + Style = New Creation
)}
); }

ایک تبصرہ شائع کریں

0 تبصرے