Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| import React from 'react' | |
| import AudioPlayer, { AudioPlayerHandle } from './AudioPlayer' | |
| import type { ExamplesData } from './Examples' | |
| import { groupByNameAndVariant, VARIANT_NAME_MAP } from './galleryUtils' | |
| import ExampleDetailsSection from './ExampleDetailsSection' | |
| import ExampleVariantSelector from './ExampleVariantSelector' | |
| import ExampleVariantMetricsTables from './ExampleVariantMetricsTable' | |
| import ExampleVariantToggle, { handleVariantToggleClick } from './ExampleVariantToggle' | |
| import LoadingSpinner from './LoadingSpinner' | |
| import API from '../API' | |
| import { InformationCircleIcon } from '@heroicons/react/24/outline' | |
| import { Tooltip as ReactTooltip } from 'react-tooltip' | |
| import 'react-tooltip/dist/react-tooltip.css' | |
| interface GalleryProps { | |
| selectedModel: string | |
| selectedAttack: string | |
| examples: { | |
| [model: string]: { | |
| [attack: string]: ExamplesData[] | |
| } | |
| } | |
| } | |
| const AudioGallery: React.FC<GalleryProps> = ({ selectedModel, selectedAttack, examples }) => { | |
| const exampleItems = examples[selectedModel][selectedAttack] | |
| const grouped = groupByNameAndVariant(exampleItems) | |
| const audioNames = Object.keys(grouped) | |
| const [selectedAudio, setSelectedAudio] = React.useState(audioNames[0] || '') | |
| const variants = grouped[selectedAudio] || {} | |
| const variantKeys = Object.keys(variants) | |
| const [selectedVariant, setSelectedVariant] = React.useState(variantKeys[0] || '') | |
| const [toggleMode, setToggleMode] = React.useState<'wmd' | 'attacked'>('wmd') | |
| // Shared playback state | |
| const playbackTimeRef = React.useRef(0) | |
| const [playingVariant, setPlayingVariant] = React.useState<string | null>(null) | |
| // Refs for all players | |
| const audioRefs = React.useMemo(() => { | |
| const refs: Record<string, React.RefObject<AudioPlayerHandle>> = {} | |
| variantKeys.forEach((v) => { | |
| refs[v] = React.createRef<AudioPlayerHandle>() | |
| }) | |
| return refs | |
| }, [variantKeys.join(',')]) | |
| // Add state for rewind seconds | |
| const [rewindSeconds, setRewindSeconds] = React.useState(0.5) | |
| const [imageLoading, setImageLoading] = React.useState(true) | |
| // Play handler: pause all others, sync time | |
| const handlePlay = (variant: string) => { | |
| console.log(`Playing variant: ${variant}`) | |
| setPlayingVariant(variant) | |
| variantKeys.forEach((v) => { | |
| if (v !== variant && audioRefs[v]?.current) { | |
| audioRefs[v]?.current?.pause() | |
| // audioRefs[v]?.current?.setTime(playbackTimeRef.current) | |
| } | |
| }) | |
| } | |
| // Pause handler | |
| const handlePause = (variant: string) => { | |
| console.log(`Pausing variant: ${variant}`) | |
| if (playingVariant === variant) setPlayingVariant(null) | |
| } | |
| React.useEffect(() => { | |
| setSelectedVariant(variantKeys[0] || '') | |
| }, [selectedAudio]) | |
| // Reset image loading state when selected variant changes | |
| React.useEffect(() => { | |
| if (variants[selectedVariant]?.image_url) { | |
| setImageLoading(true) | |
| } | |
| }, [selectedVariant, variants[selectedVariant]?.image_url]) | |
| // When selectedVariant changes, play that variant and pause others, syncing position | |
| React.useEffect(() => { | |
| if (!selectedVariant) { | |
| return | |
| } | |
| if (playingVariant == null) { | |
| // On page load don't auto play, only when swapping tracks | |
| return | |
| } | |
| // Rewind playbackTimeRef by rewindSeconds, clamp to 0 | |
| playbackTimeRef.current = Math.max(0, playbackTimeRef.current - rewindSeconds) | |
| setPlayingVariant(selectedVariant) | |
| variantKeys.forEach((v) => { | |
| if (v !== selectedVariant) { | |
| audioRefs[v]?.current?.pause() | |
| } | |
| if (audioRefs[v]?.current) { | |
| audioRefs[v]?.current?.setTime(playbackTimeRef.current) | |
| } | |
| }) | |
| }, [selectedVariant]) | |
| console.log(audioRefs[selectedVariant]?.current?.getCurrentTime()) | |
| if (!audioNames.length) { | |
| return ( | |
| <div className="w-full mt-12 flex items-center justify-center"> | |
| <div className="text-gray-500"> | |
| No audio examples available. Please select another model and attack. | |
| </div> | |
| </div> | |
| ) | |
| } | |
| return ( | |
| <div className="w-full overflow-auto" style={{ minHeight: '100vh' }}> | |
| <div className="example-display"> | |
| <div className="mb-4"> | |
| <fieldset className="fieldset"> | |
| <legend className="fieldset-legend">Audio</legend> | |
| <select | |
| className="select select-bordered" | |
| value={selectedAudio || ''} | |
| onChange={(e) => { | |
| setSelectedAudio(e.target.value || '') | |
| }} | |
| > | |
| {audioNames.map((name) => ( | |
| <option key={name} value={name}> | |
| {name} | |
| </option> | |
| ))} | |
| </select> | |
| </fieldset> | |
| </div> | |
| {selectedAudio && selectedVariant && variants[selectedVariant] && ( | |
| <> | |
| <ExampleVariantMetricsTables | |
| variantMetadatas={Object.fromEntries( | |
| variantKeys.map((v) => [v, variants[v]?.metadata || {}]) | |
| )} | |
| /> | |
| <ExampleDetailsSection> | |
| <ExampleVariantSelector | |
| variantKeys={variantKeys} | |
| selectedVariant={selectedVariant} | |
| setSelectedVariant={setSelectedVariant} | |
| /> | |
| <ReactTooltip | |
| id="variant-selector-tooltip" | |
| place="top" | |
| className="z-[10000] max-w-xs !opacity-100 bg-base-100 text-base-content" | |
| style={{ boxShadow: '0 0 10px rgba(0,0,0,0.2)', zIndex: 10000 }} | |
| positionStrategy="fixed" | |
| > | |
| <div className="p-2 text-xs text-left relative z-[10000]"> | |
| You can also change the variant using keys <b>1</b>, <b>2</b>, <b>3</b>, <b>4</b>{' '} | |
| on your keyboard. | |
| </div> | |
| </ReactTooltip> | |
| <ExampleVariantToggle | |
| toggleMode={toggleMode} | |
| setToggleMode={setToggleMode} | |
| selectedVariant={selectedVariant} | |
| setSelectedVariant={setSelectedVariant} | |
| variantKeys={variantKeys} | |
| /> | |
| <fieldset className="fieldset mt-2"> | |
| <legend className="fieldset-legend">Rewind Seconds</legend> | |
| <input | |
| id="rewind-seconds" | |
| type="number" | |
| min={0} | |
| step={0.1} | |
| value={rewindSeconds} | |
| onChange={(e) => setRewindSeconds(Math.max(0, Number(e.target.value)))} | |
| className="input input-bordered w-20" | |
| placeholder="Seconds" | |
| /> | |
| </fieldset> | |
| <div className="flex flex-col items-center gap-4"> | |
| {variantKeys.map((variantKey) => | |
| variants[variantKey].audio_url ? ( | |
| <div | |
| key={variantKey} | |
| style={{ | |
| width: '100%', | |
| display: selectedVariant === variantKey ? 'block' : 'none', | |
| }} | |
| > | |
| <div className="font-mono text-xs mb-1">{variantKey}</div> | |
| <AudioPlayer | |
| ref={audioRefs[variantKey]} | |
| src={API.getProxiedUrl(variants[variantKey].audio_url, true)} | |
| playing={playingVariant === variantKey} | |
| onPlay={() => handlePlay(variantKey)} | |
| onPause={() => handlePause(variantKey)} | |
| onAudioProcess={(currentTime) => { | |
| playbackTimeRef.current = currentTime | |
| variantKeys.forEach((v) => { | |
| if (v !== variantKey && audioRefs[v]?.current) { | |
| audioRefs[v]?.current?.setTime(currentTime) | |
| } | |
| }) | |
| }} | |
| /> | |
| </div> | |
| ) : null | |
| )} | |
| {variants[selectedVariant].image_url && ( | |
| <div className="relative"> | |
| {imageLoading && <LoadingSpinner />} | |
| <img | |
| key={variants[selectedVariant].image_url} | |
| src={API.getProxiedUrl(variants[selectedVariant].image_url)} | |
| alt={selectedAudio} | |
| className="example-image" | |
| style={{ | |
| display: 'block', | |
| opacity: imageLoading ? 0 : 1, | |
| transition: 'opacity 0.3s', | |
| }} | |
| onLoad={() => setImageLoading(false)} | |
| onError={() => setImageLoading(false)} | |
| onClick={() => | |
| handleVariantToggleClick( | |
| toggleMode, | |
| selectedVariant, | |
| setSelectedVariant, | |
| variantKeys | |
| ) | |
| } | |
| /> | |
| </div> | |
| )} | |
| </div> | |
| </ExampleDetailsSection> | |
| </> | |
| )} | |
| </div> | |
| </div> | |
| ) | |
| } | |
| export default AudioGallery | |