Download src/components/controls/PlaybackControls.tsx from TechAvenger/kernel-convolution-visualizer: direct link, hf CLI and curl.
- Browser
- Download file 5.65 kB
-
https://huggingface.co/spaces/TechAvenger/kernel-convolution-visualizer/resolve/25e773913dbd4e9dcd6b40f787d68b280ccdfca1/src/components/controls/PlaybackControls.tsx
- Command line
-
hf download hf://spaces/TechAvenger/kernel-convolution-visualizer@25e773913dbd4e9dcd6b40f787d68b280ccdfca1/src/components/controls/PlaybackControls.tsx
-
curl -L -o PlaybackControls.tsx https://huggingface.co/spaces/TechAvenger/kernel-convolution-visualizer/resolve/25e773913dbd4e9dcd6b40f787d68b280ccdfca1/src/components/controls/PlaybackControls.tsx
5.65 kB
| 'use client' | |
| import { useAppStore } from '@/store' | |
| import { useConvolution } from '@/hooks/useConvolution' | |
| import { Button, Slider, Card, CardContent } from '@/components/ui' | |
| import { cn } from '@/utils/helpers' | |
| import { Play, Pause, RotateCcw, StepForward, ZoomIn, ZoomOut, Maximize } from 'lucide-react' | |
| export function PlaybackControls() { | |
| const { | |
| convolution, | |
| settings, | |
| startConvolution, | |
| pauseConvolution, | |
| resumeConvolution, | |
| stopConvolution, | |
| updateConvolutionStep, | |
| updateConvolutionProgress, | |
| completeConvolution, | |
| setConvolutionError, | |
| resetConvolution, | |
| setAnimationSpeed, | |
| setZoomLevel, | |
| setPanOffset, | |
| toggleOverlay, | |
| setPadding, | |
| setComparisonMode, | |
| setStepMode, | |
| } = useAppStore() | |
| const { runConvolution, pause, resume, stop, step, reset, isRunning, isPaused, canRun, toggleStepMode } = useConvolution() | |
| const { status, progress, elapsedTime } = convolution | |
| const { animationSpeed, zoomLevel, stepMode } = settings | |
| const isCompleted = status === 'completed' | |
| const isActive = isRunning || isPaused || isCompleted | |
| const formatTime = (ms: number) => { | |
| if (ms < 1000) return `${ms}ms` | |
| return `${(ms / 1000).toFixed(1)}s` | |
| } | |
| return ( | |
| <Card className="w-full"> | |
| <CardContent className="p-4 space-y-4"> | |
| {/* Main Controls */} | |
| <div className="flex items-center gap-2 flex-wrap"> | |
| <Button | |
| variant={isRunning ? 'secondary' : 'primary'} | |
| size="lg" | |
| onClick={isRunning ? pause : runConvolution} | |
| disabled={!canRun} | |
| className="min-w-[100px]" | |
| > | |
| {isRunning ? ( | |
| <> | |
| <Pause className="h-4 w-4" /> | |
| Pause | |
| </> | |
| ) : ( | |
| <> | |
| <Play className="h-4 w-4" /> | |
| {isPaused ? 'Resume' : 'Play'} | |
| </> | |
| )} | |
| </Button> | |
| <Button variant="outline" size="lg" onClick={step} disabled={isRunning || isCompleted}> | |
| <StepForward className="h-4 w-4" /> | |
| Step | |
| </Button> | |
| <Button variant="outline" size="lg" onClick={stop} disabled={status === 'idle'}> | |
| <RotateCcw className="h-4 w-4" /> | |
| Stop | |
| </Button> | |
| <Button variant="outline" size="lg" onClick={reset} disabled={status === 'idle'}> | |
| <RotateCcw className="h-4 w-4" /> | |
| Reset | |
| </Button> | |
| </div> | |
| {/* Speed Control */} | |
| <div> | |
| <label className="flex items-center justify-between text-sm mb-1"> | |
| <span>Animation Speed</span> | |
| <span className="font-mono text-blue-600">{animationSpeed.toFixed(1)}x</span> | |
| </label> | |
| <Slider | |
| min={0.1} | |
| max={10} | |
| step={0.1} | |
| value={animationSpeed} | |
| onValueChange={setAnimationSpeed} | |
| showValue | |
| valueFormat={(v: number) => `${v.toFixed(1)}x`} | |
| /> | |
| </div> | |
| {/* Zoom Controls */} | |
| <div> | |
| <label className="flex items-center justify-between text-sm mb-1"> | |
| <span>Zoom</span> | |
| <span className="font-mono text-blue-600">{Math.round(zoomLevel * 100)}%</span> | |
| </label> | |
| <Slider | |
| min={0.1} | |
| max={20} | |
| step={0.1} | |
| value={zoomLevel} | |
| onValueChange={setZoomLevel} | |
| showValue | |
| valueFormat={(v: number) => `${Math.round(v * 100)}%`} | |
| /> | |
| <div className="flex gap-2 mt-2"> | |
| <Button variant="outline" size="sm" onClick={() => setZoomLevel(Math.min(20, zoomLevel * 1.2))}> | |
| <ZoomIn className="h-4 w-4" /> | |
| </Button> | |
| <Button variant="outline" size="sm" onClick={() => setZoomLevel(Math.max(0.1, zoomLevel / 1.2))}> | |
| <ZoomOut className="h-4 w-4" /> | |
| </Button> | |
| <Button variant="outline" size="sm" onClick={() => { setZoomLevel(1); setPanOffset({ x: 0, y: 0 }) }}> | |
| <Maximize className="h-4 w-4" /> | |
| Fit | |
| </Button> | |
| </div> | |
| </div> | |
| {/* Progress */} | |
| {isActive && ( | |
| <div className="space-y-2 pt-2 border-t dark:border-gray-700"> | |
| <div className="flex items-center justify-between text-sm"> | |
| <span>Progress</span> | |
| <span className="font-mono">{Math.round(progress * 100)}%</span> | |
| </div> | |
| <div className="h-2 bg-gray-200 rounded-full overflow-hidden dark:bg-gray-700"> | |
| <div | |
| className="h-full bg-blue-600 transition-all duration-300" | |
| style={{ width: `${progress * 100}%` }} | |
| /> | |
| </div> | |
| <div className="flex justify-between text-xs text-gray-500"> | |
| <span>Elapsed: {formatTime(elapsedTime)}</span> | |
| <span>{status === 'running' ? 'Running' : status === 'paused' ? 'Paused' : 'Completed'}</span> | |
| </div> | |
| </div> | |
| )} | |
| {/* Step Mode Toggle */} | |
| <div className="flex items-center justify-between pt-2 border-t dark:border-gray-700"> | |
| <label className="flex items-center gap-2 cursor-pointer"> | |
| <input | |
| type="checkbox" | |
| checked={stepMode} | |
| onChange={(e) => toggleStepMode(e.target.checked)} | |
| className="rounded border-gray-300" | |
| /> | |
| <span className="text-sm">Step Mode (manual advance)</span> | |
| </label> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ) | |
| } |