Xenova's picture
Xenova HF Staff
Upload 18 files
2beb552 verified
raw
history blame
5.39 kB
import React, { useRef } from "react";
import { Play, Pause, Download } from "lucide-react";
interface AudioResultProps {
stats: {
firstLatency: number | null;
processingTime: number;
charsPerSec: number;
rtf: number;
totalDuration: number;
currentDuration: number;
};
progressPercentage: number;
isGenerating: boolean;
isPlaying: boolean;
onTogglePlay: () => void;
onDownload: () => void;
onSeek: (percentage: number) => void;
}
export const AudioResult = ({
stats,
progressPercentage,
isGenerating,
isPlaying,
onTogglePlay,
onDownload,
onSeek,
}: AudioResultProps) => {
const progressBarRef = useRef<HTMLDivElement>(null);
const formatTime = (secs: number) => {
const minutes = Math.floor(secs / 60);
const seconds = (secs % 60).toFixed(2);
return `${minutes}:${seconds.padStart(5, "0")}`;
};
const playbackProgress =
stats.totalDuration > 0 ? Math.min(100, (stats.currentDuration / stats.totalDuration) * 100) : 0;
const handleSeekClick = (e: React.MouseEvent<HTMLDivElement>) => {
if (progressBarRef.current) {
const rect = progressBarRef.current.getBoundingClientRect();
const x = e.clientX - rect.left;
const percentage = Math.max(0, Math.min(1, x / rect.width));
onSeek(percentage);
}
};
return (
<div className="mt-8 relative rounded-xl overflow-hidden animate-in fade-in slide-in-from-top-4 duration-500 border border-gray-200">
<div
className="absolute top-0 left-0 bottom-0 bg-[#E5E7EB] transition-all duration-500 ease-out -z-10"
style={{ width: `${progressPercentage}%` }}
/>
<div className="absolute top-0 left-0 w-full h-full bg-gray-50 -z-20" />
<div className="p-4 flex flex-col md:flex-row items-center gap-6 relative z-10">
<div className="flex flex-col min-w-[80px]">
<span className="text-blue-600 font-semibold text-lg leading-tight">Supertonic</span>
<span className="text-gray-600 text-sm font-medium">On-Device</span>
</div>
<div className="flex-1 grid grid-cols-3 gap-x-8 gap-y-1 text-center border-l border-r border-gray-300 px-4 md:px-8">
<div className="flex flex-col items-center">
<div className="font-mono text-gray-900 text-lg tracking-tight">
{stats.firstLatency !== null ? (
<>
<span className="text-[10px] text-gray-500 font-sans font-bold uppercase mr-1 align-middle">
First
</span>
{stats.firstLatency.toFixed(2)}
<span className="text-sm text-gray-500">s</span>
<span className="mx-1 text-gray-400">/</span>
</>
) : null}
{stats.processingTime.toFixed(2)}
<span className="text-sm text-gray-500">s</span>
</div>
<div className="text-[10px] text-gray-500 uppercase font-bold tracking-wider mt-1">Processing Time ↓</div>
</div>
<div className="flex flex-col items-center">
<div className="font-mono text-gray-900 text-lg tracking-tight">
{stats.charsPerSec > 0 ? stats.charsPerSec.toFixed(1) : "-"}
</div>
<div className="text-[10px] text-gray-500 uppercase font-bold tracking-wider mt-1">Chars/sec ↑</div>
</div>
<div className="flex flex-col items-center">
<div className="font-mono text-gray-900 text-lg tracking-tight">
{stats.rtf > 0 ? stats.rtf.toFixed(3) : "-"}
<span className="text-sm text-gray-500">x</span>
</div>
<div className="text-[10px] text-gray-500 uppercase font-bold tracking-wider mt-1">RTF ↓</div>
</div>
</div>
<div className="flex items-center gap-4 min-w-[300px] w-full md:w-auto">
<button
onClick={onTogglePlay}
className={`w-10 h-10 rounded-full flex items-center justify-center transition-colors flex-shrink-0 text-gray-800 bg-white hover:bg-gray-100 shadow-sm`}
>
{isPlaying ? (
<Pause size={18} fill="currentColor" />
) : (
<Play size={18} fill="currentColor" className="ml-0.5" />
)}
</button>
<span className="font-mono text-xs text-gray-600 w-[50px] text-right">
{formatTime(stats.currentDuration)}
</span>
<div
ref={progressBarRef}
className="relative flex-1 h-1.5 bg-gray-300 rounded-full overflow-hidden min-w-[100px] cursor-pointer hover:h-2 transition-all group"
onClick={handleSeekClick}
>
<div
className="absolute top-0 left-0 h-full bg-blue-500 rounded-full group-hover:bg-blue-600"
style={{ width: `${playbackProgress}%` }}
/>
</div>
<span className="font-mono text-xs text-gray-600 w-[50px]">{formatTime(stats.totalDuration)}</span>
<button
onClick={onDownload}
disabled={isGenerating}
className={`p-2 rounded-full text-gray-700 transition-colors ${isGenerating ? "opacity-50 cursor-not-allowed" : "hover:bg-gray-200"}`}
>
<Download size={18} />
</button>
</div>
</div>
</div>
);
};