const { useState } = React; const FaceList = ({ faces, onSeek }) => { const [expandedFaceId, setExpandedFaceId] = useState(null); const toggleFace = (faceId) => { setExpandedFaceId(expandedFaceId === faceId ? null : faceId); }; const parseTime = (timeStr) => { if (typeof timeStr === 'number') return timeStr; if (!timeStr) return 0; const [h, m, s] = timeStr.split(':'); return parseInt(h) * 3600 + parseInt(m) * 60 + parseFloat(s); }; // Helper to safely get name (handles object with .value or string) const getName = (face) => { const n = face.name; if (typeof n === 'object' && n !== null) return n.value || face.face_id; return n || face.face_id; }; if (!faces || faces.length === 0) { return (

No face detection data available.

); } return (

Detected Faces ({faces.length})

{faces.map((face) => { const isExpanded = expandedFaceId === face.face_id; const bestFaceTime = face.timestamp_of_best_face; const ocrMatch = face.ocr_name_match; const displayName = getName(face); return (
{/* Header / Summary Line */}
toggleFace(face.face_id)} className="p-4 cursor-pointer flex justify-between items-start gap-4" >
{displayName && face.face_id && displayName !== face.face_id ? `${displayName} (${face.face_id})` : displayName} {face.confidence != null && ( {(face.confidence * 100).toFixed(0)}% )}
{face.title && (
{face.title}
)}
{face.description}
{/* Details (Expanded) */} {isExpanded && (
{/* Quick Actions */}
{bestFaceTime && ( )} {ocrMatch && ocrMatch.matched && ( )}
{/* Screen Time Segments */}
Appearances
{face.screen_time.map((segment, idx) => (
{/* Start Group */}
{/* End Group */}
))}
)}
); })}
); }; window.FaceList = FaceList;