Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| import React, { useState, useEffect, useRef } from 'react' | |
| import { useLocation, useNavigate } from 'react-router-dom' | |
| import API from '../API' | |
| import LoadingSpinner from './LoadingSpinner' | |
| import ImageGallery from './ImageGallery' | |
| import AudioGallery from './AudioGallery' | |
| import VideoGallery from './VideoGallery' | |
| import ModelInfoIcon from './ModelInfoIcon' | |
| import Descriptions from '../Descriptions' | |
| interface ExamplesProps { | |
| fileType: 'image' | 'audio' | 'video' | |
| } | |
| // Move ExamplesData type export to allow import in Galleries.tsx | |
| export type ExamplesData = { | |
| image_url: string | |
| audio_url?: string | |
| video_url?: string | |
| name: string | |
| metadata: { | |
| [key: string]: string | boolean | |
| } | |
| } | |
| const Examples = ({ fileType }: ExamplesProps) => { | |
| const [examples, setExamples] = useState<{ | |
| [model: string]: { [attack: string]: ExamplesData[] } | |
| }>({}) | |
| const [loading, setLoading] = useState(false) | |
| const [error, setError] = useState<string | null>(null) | |
| const [selectedModel, setSelectedModel] = useState<string | null>(null) | |
| const [selectedAttack, setSelectedAttack] = useState<string | null>(null) | |
| const [descriptionsLoaded, setDescriptionsLoaded] = useState(false) | |
| const descriptions = useRef(Descriptions.getInstance()) | |
| useEffect(() => { | |
| descriptions.current.load().then(() => setDescriptionsLoaded(true)) | |
| }, []) | |
| const location = useLocation() | |
| // Parse query params for model and attack | |
| useEffect(() => { | |
| const params = new URLSearchParams(location.search) | |
| const modelParam = params.get('model') | |
| const attackParam = params.get('attack') | |
| const strengthParam = params.get('strength') | |
| if (modelParam) setSelectedModel(modelParam) | |
| // Find the most appropriate selectedAttack | |
| if (attackParam || strengthParam) { | |
| setSelectedAttack((prev) => { | |
| if (!selectedModel || !examples[selectedModel]) return prev | |
| const attacks = Object.keys(examples[selectedModel]) | |
| // If both attack and strength are present, look for attack containing attackParam and ending with strengthParam | |
| if (attackParam && strengthParam) { | |
| const found = attacks.find((a) => a.includes(attackParam) && a.endsWith(strengthParam)) | |
| if (found) return found | |
| } | |
| // If only attack is present, look for attack containing attackParam | |
| if (attackParam) { | |
| const found = attacks.find((a) => a.includes(attackParam)) | |
| if (found) return found | |
| } | |
| // If only strength is present, look for attack ending with strengthParam | |
| if (strengthParam) { | |
| const found = attacks.find((a) => a.endsWith(strengthParam)) | |
| if (found) return found | |
| } | |
| return prev | |
| }) | |
| } | |
| }, [location.search, selectedModel, examples]) | |
| useEffect(() => { | |
| setLoading(true) | |
| setError(null) | |
| API.fetchExamplesByType(fileType) | |
| .then((data) => { | |
| setExamples(data) | |
| const models = Object.keys(data) | |
| if (models.length > 0) { | |
| // If query param exists and is valid, use it, else default to first | |
| setSelectedModel((prev) => (prev && data[prev] ? prev : models[0])) | |
| const attacks = Object.keys(data[models[0]]) | |
| if (attacks.length > 0) { | |
| setSelectedAttack((prev) => (prev && data[models[0]][prev] ? prev : attacks[0])) | |
| } else { | |
| setSelectedAttack(null) | |
| } | |
| } else { | |
| setSelectedModel(null) | |
| setSelectedAttack(null) | |
| } | |
| setLoading(false) | |
| }) | |
| .catch((err) => { | |
| setError(err.message) | |
| setLoading(false) | |
| }) | |
| }, [fileType]) | |
| if (loading) { | |
| return <LoadingSpinner /> | |
| } | |
| return ( | |
| <div className="examples-container"> | |
| <div className="selectors-container flex flex-col gap-4"> | |
| <fieldset className="fieldset w-full p-4 rounded border border-gray-700 bg-base-200"> | |
| <legend className="fieldset-legend font-semibold">Model</legend> | |
| <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-1 max-h-48 overflow-y-auto pr-2"> | |
| {Object.keys(examples).map((model) => { | |
| const fullName = descriptions.current.getModelFullName(model) || model | |
| const modelAlias = descriptions.current.getModelAlias(model) || model | |
| return ( | |
| <div key={model} className="flex items-center gap-2 text-sm relative group"> | |
| <label className="flex items-center gap-2 flex-grow"> | |
| <input | |
| type="radio" | |
| className="radio radio-sm" | |
| checked={selectedModel === model} | |
| onChange={() => setSelectedModel(model)} | |
| name="model-selection" | |
| /> | |
| <div className="flex items-center"> | |
| <span className="truncate" title={fullName}> | |
| {modelAlias} | |
| </span> | |
| <ModelInfoIcon modelName={model} /> | |
| </div> | |
| </label> | |
| </div> | |
| ) | |
| })} | |
| </div> | |
| </fieldset> | |
| {selectedModel && ( | |
| <fieldset className="fieldset"> | |
| <legend className="fieldset-legend">Attack</legend> | |
| <select | |
| className="select select-bordered" | |
| value={selectedAttack || ''} | |
| onChange={(e) => setSelectedAttack(e.target.value || null)} | |
| > | |
| {Object.keys(examples[selectedModel]).map((attack) => ( | |
| <option key={attack} value={attack}> | |
| {attack} | |
| </option> | |
| ))} | |
| </select> | |
| </fieldset> | |
| )} | |
| </div> | |
| {error && <p className="error">Error: {error}</p>} | |
| {selectedModel && selectedAttack && fileType === 'image' && ( | |
| <ImageGallery | |
| selectedModel={selectedModel} | |
| selectedAttack={selectedAttack} | |
| examples={examples} | |
| /> | |
| )} | |
| {selectedModel && selectedAttack && fileType === 'audio' && ( | |
| <AudioGallery | |
| selectedModel={selectedModel} | |
| selectedAttack={selectedAttack} | |
| examples={examples} | |
| /> | |
| )} | |
| {selectedModel && selectedAttack && fileType === 'video' && ( | |
| <VideoGallery | |
| selectedModel={selectedModel} | |
| selectedAttack={selectedAttack} | |
| examples={examples} | |
| /> | |
| )} | |
| </div> | |
| ) | |
| } | |
| export default Examples | |