/** * FaceSwapRefInput — inline reference image picker for Face Swap. * * Additive component — rendered below the Face Swap button in IdentityTools * when user clicks Face Swap and no reference is yet set. * * Compact inline variant — upload zone + URL input. * Same pattern as AvatarStudio reference upload. */ import React, { useState, useCallback, useRef } from 'react'; import { Upload, X, Loader2, Check } from 'lucide-react'; import { resolveFileUrl } from '../resolveFileUrl'; // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export function FaceSwapRefInput({ backendUrl, apiKey, onReferenceReady, onCancel, }) { const [uploading, setUploading] = useState(false); const [preview, setPreview] = useState(null); const [urlInput, setUrlInput] = useState(''); const [error, setError] = useState(null); const fileInputRef = useRef(null); const handleFileUpload = useCallback(async (file) => { setUploading(true); setError(null); // Show local preview immediately const localPreview = URL.createObjectURL(file); setPreview(localPreview); // Upload to backend const formData = new FormData(); formData.append('file', file); const base = (backendUrl || '').replace(/\/+$/, ''); const headers = {}; if (apiKey) headers['x-api-key'] = apiKey; try { const res = await fetch(`${base}/upload`, { method: 'POST', headers, body: formData, }); if (res.ok) { const data = await res.json(); const uploadedUrl = data.url || data.file_url || ''; if (uploadedUrl) { onReferenceReady(uploadedUrl); } else { setError('Upload succeeded but no URL returned'); } } else { setError(`Upload failed: ${res.status}`); } } catch (e) { setError(e instanceof Error ? e.message : 'Upload failed'); } finally { setUploading(false); } }, [backendUrl, apiKey, onReferenceReady]); const handleUrlSubmit = useCallback(() => { const trimmed = urlInput.trim(); if (trimmed) { onReferenceReady(trimmed); } }, [urlInput, onReferenceReady]); return (
Upload the face to swap onto this image
{/* Upload button */} { const file = e.target.files?.[0]; if (file) handleFileUpload(file); e.target.value = ''; }}/> {/* Preview thumbnail */} {preview && !uploading && (
Face preview
)} {/* URL input */} {!preview && !uploading && (
or setUrlInput(e.target.value)} placeholder="Paste image URL..." className="flex-1 px-2 py-1.5 rounded-lg bg-white/5 border border-white/10 text-white text-[11px] placeholder:text-white/20 focus:outline-none focus:border-cyan-500/50 transition-all" onKeyDown={(e) => { if (e.key === 'Enter') handleUrlSubmit(); }}/> {urlInput.trim() && ()}
)}
{error && (
{error}
)}
); }