import React from 'react' import type { ExamplesData } from './Examples' import { groupByNameAndVariant } from './galleryUtils' import ExampleVariantMetricsTable from './ExampleVariantMetricsTable' import ExampleDetailsSection from './ExampleDetailsSection' import ExampleVariantSelector from './ExampleVariantSelector' import ExampleVariantToggle from './ExampleVariantToggle' interface GalleryProps { selectedModel: string selectedAttack: string examples: { [model: string]: { [attack: string]: ExamplesData[] } } } const VideoGallery: React.FC = ({ selectedModel, selectedAttack, examples }) => { const exampleItems = examples[selectedModel][selectedAttack] const grouped = groupByNameAndVariant(exampleItems) const videoNames = Object.keys(grouped) const [selectedVideo, setSelectedVideo] = React.useState(videoNames[0] || '') const variants = grouped[selectedVideo] || {} const variantKeys = Object.keys(variants) const [selectedVariant, setSelectedVariant] = React.useState(variantKeys[0] || '') const [toggleMode, setToggleMode] = React.useState<'wmd' | 'attacked'>('wmd') // Add state for rewind seconds const [rewindSeconds, setRewindSeconds] = React.useState(0.5) // State for video scale const [videoScale, setVideoScale] = React.useState(1) // Playback time ref for syncing position const playbackTimeRef = React.useRef(0) // Refs for all video elements const videoRefs = React.useMemo(() => { const refs: Record> = {} variantKeys.forEach((v) => { refs[v] = React.createRef() }) return refs }, [variantKeys.join(',')]) // Track which variant is currently playing const [playingVariant, setPlayingVariant] = React.useState(null) // Play handler: pause all others, sync time const handlePlay = (variant: string) => { setPlayingVariant(variant) variantKeys.forEach((v) => { if (v !== variant && videoRefs[v]?.current) { videoRefs[v]?.current?.pause() } }) } // Pause handler const handlePause = (variant: string) => { if (playingVariant === variant) setPlayingVariant(null) } // When selectedVariant changes, sync playback position, rewind, and play state React.useEffect(() => { if (!selectedVariant) return // Rewind playbackTimeRef by rewindSeconds, clamp to 0 playbackTimeRef.current = Math.max(0, playbackTimeRef.current - rewindSeconds) // Set all videos to the new time, pause all except selected variantKeys.forEach((v) => { const ref = videoRefs[v]?.current if (ref) { ref.currentTime = playbackTimeRef.current if (v !== selectedVariant) { ref.pause() } } }) // If a video was playing, continue playing the swapped variant if (playingVariant && videoRefs[selectedVariant]?.current) { videoRefs[selectedVariant].current.play() } }, [selectedVariant]) // When the selected video plays, update playbackTimeRef const handleTimeUpdate = (variantKey: string) => { const ref = videoRefs[variantKey]?.current if (ref && variantKey === selectedVariant) { playbackTimeRef.current = ref.currentTime } } React.useEffect(() => { setSelectedVariant(variantKeys[0] || '') }, [selectedVideo]) if (!videoNames.length) { return (
No video examples available. Please select another model and attack.
) } return (
Video Example
{selectedVideo && selectedVariant && variants[selectedVariant] && ( <> [v, variants[v]?.metadata || {}]) )} />
setRewindSeconds(Math.max(0, Number(e.target.value)))} className="input input-bordered input-xs w-20" placeholder="Seconds" /> setVideoScale(Number(e.target.value))} className="range range-xs w-40" style={{ verticalAlign: 'middle' }} /> {(videoScale * 100).toFixed(0)}%
{variantKeys.map((variantKey) => variants[variantKey].video_url ? (
)}
) } export default VideoGallery