import React, { useState, useRef, useEffect } from 'react'; import { Rect } from '../types'; interface RiggingEditorProps { imageUrl: string; initialData?: { leftEye: Rect; rightEye: Rect; mouth: Rect; skinColor: string }; onComplete: (data: { leftEye: Rect; rightEye: Rect; mouth: Rect; skinColor: string }) => void; } type ActiveFeature = 'leftEye' | 'rightEye' | 'mouth' | null; const ResizableBox: React.FC<{ rect: Rect; color: string; label: string; isActive: boolean; onUpdate: (rect: Rect) => void; onActivate: () => void; }> = ({ rect, color, label, isActive, onUpdate, onActivate }) => { const boxRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [isResizing, setIsResizing] = useState(false); const startPos = useRef({ x: 0, y: 0 }); const startRect = useRef({ x: 0, y: 0, w: 0, h: 0 }); const handleMouseDown = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); onActivate(); setIsDragging(true); startPos.current = { x: e.clientX, y: e.clientY }; startRect.current = { ...rect }; }; const handleResizeDown = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); onActivate(); setIsResizing(true); startPos.current = { x: e.clientX, y: e.clientY }; startRect.current = { ...rect }; }; useEffect(() => { const handleMouseMove = (e: MouseEvent) => { if (!isDragging && !isResizing) return; const parent = boxRef.current?.parentElement; if (!parent) return; const parentRect = parent.getBoundingClientRect(); const deltaX = (e.clientX - startPos.current.x) / parentRect.width; const deltaY = (e.clientY - startPos.current.y) / parentRect.height; if (isDragging) { onUpdate({ ...rect, x: startRect.current.x + deltaX, y: startRect.current.y + deltaY, }); } else if (isResizing) { onUpdate({ ...rect, w: Math.max(0.01, startRect.current.w + deltaX), h: Math.max(0.01, startRect.current.h + deltaY), }); } }; const handleMouseUp = () => { setIsDragging(false); setIsResizing(false); }; if (isDragging || isResizing) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); } return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging, isResizing, rect, onUpdate]); return (
{/* Label */}
{label}
{/* Resize Handle */}
); }; const RiggingEditor: React.FC = ({ imageUrl, initialData, onComplete }) => { const [leftEye, setLeftEye] = useState(initialData?.leftEye || { x: 0.35, y: 0.4, w: 0.12, h: 0.08 }); const [rightEye, setRightEye] = useState(initialData?.rightEye || { x: 0.53, y: 0.4, w: 0.12, h: 0.08 }); const [mouth, setMouth] = useState(initialData?.mouth || { x: 0.45, y: 0.6, w: 0.1, h: 0.05 }); const [skinColor, setSkinColor] = useState(initialData?.skinColor || '#fcd3bf'); const [activeFeature, setActiveFeature] = useState(null); return (

Rig Your Avatar

Drag and resize the boxes to match your avatar's features. This ensures the eyes blink correctly.

{/* Editor Area */}
Rigging Target setActiveFeature('leftEye')} /> setActiveFeature('rightEye')} /> setActiveFeature('mouth')} />
{/* Sidebar Controls */}
setSkinColor(e.target.value)} className="w-10 h-10 rounded cursor-pointer border-0 p-0" /> {skinColor}

Pick the color of the skin above the eyes for realistic blinking.

Left Eye Box
Right Eye Box
Mouth Box
); }; export default RiggingEditor;