/** * IdentityTools - Optional identity-aware edit buttons for Edit Studio. * * Additive + non-destructive: * - Only rendered when avatar identity models are installed * - Does NOT replace or modify existing Quick Enhance / Background tools * - Controlled by parent via `show` / capability booleans * * Basic Pack (InsightFace + InstantID): * - Fix Faces+ (identity-aware face restoration) * - Inpaint (Preserve Person) * - Change BG (Preserve Person) * * Full Pack (+ InSwapper): * - Face Swap (NOW FUNCTIONAL — uses ReActor/InSwapper workflow) */ import React, { useState } from 'react'; import { Loader2, UserCheck, Scan, ImageOff, Repeat, Shield } from 'lucide-react'; import { applyIdentityTool, IDENTITY_TOOLS, } from '../enhance/identityApi'; import { FaceSwapRefInput } from './FaceSwapRefInput'; // --------------------------------------------------------------------------- // Icon map // --------------------------------------------------------------------------- function getIcon(id) { switch (id) { case 'fix_faces_identity': return ; case 'inpaint_identity': return ; case 'change_bg_identity': return ; case 'face_swap': return ; default: return ; } } // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export function IdentityTools({ backendUrl, apiKey, imageUrl, onResult, onError, disabled = false, hasBasicIdentity, hasFaceSwap, maskDataUrl, }) { const [loading, setLoading] = useState(null); // Face Swap reference input state (additive) const [showFaceSwapRef, setShowFaceSwapRef] = useState(false); const [faceSwapRefUrl, setFaceSwapRefUrl] = useState(null); // Don't render at all if no identity models installed if (!hasBasicIdentity) return null; const anyLoading = loading !== null; const isDisabled = disabled || !imageUrl; const handleTool = async (toolType, referenceUrl) => { if (!imageUrl || anyLoading || isDisabled) return; // For face_swap, require a reference image — show picker if not set if (toolType === 'face_swap' && !referenceUrl && !faceSwapRefUrl) { setShowFaceSwapRef(true); return; } const effectiveRefUrl = referenceUrl || faceSwapRefUrl; setLoading(toolType); try { const result = await applyIdentityTool({ backendUrl, apiKey, imageUrl, toolType, referenceImageUrl: toolType === 'face_swap' ? effectiveRefUrl || undefined : undefined, maskDataUrl: toolType === 'inpaint_identity' && maskDataUrl ? maskDataUrl : undefined, }); const resultUrl = result?.media?.images?.[0]; if (resultUrl) { onResult(resultUrl, toolType); // Reset face swap state after successful swap if (toolType === 'face_swap') { setFaceSwapRefUrl(null); setShowFaceSwapRef(false); } } else { onError('Identity tool completed but no image was returned.'); } } catch (e) { onError(e instanceof Error ? e.message : 'Identity tool failed'); } finally { setLoading(null); } }; // Filter visible tools based on installed models const visibleTools = IDENTITY_TOOLS.filter((tool) => { if (tool.pack === 'full') return hasFaceSwap; return true; // basic pack tools always visible when hasBasicIdentity }); return (
{/* Section header */}
Identity Tools Beta

Edit while preserving facial identity. Uses installed Avatar & Identity models.

{/* Tool buttons */}
{visibleTools.map((tool) => (
{/* Face Swap reference input — shown inline below the Face Swap button */} {tool.id === 'face_swap' && showFaceSwapRef && ( { setFaceSwapRefUrl(url); setShowFaceSwapRef(false); // Auto-trigger the swap immediately handleTool('face_swap', url); }} onCancel={() => setShowFaceSwapRef(false)}/>)}
))}
); }