/**
* Performance Metrics Component
*
* Developer-focused dashboard showing:
* - Real-time inference speed (tokens/sec, RTF)
* - Progressive update latency
* - Window state (growing vs sliding)
* - Memory usage
*/
import { useState, useEffect } from 'react';
export default function PerformanceMetrics({
latency,
rtf,
audioDuration,
windowState,
device,
updateInterval,
isProcessingFile,
fileDuration,
transcribedDuration,
}) {
const [memoryUsage, setMemoryUsage] = useState(null);
const [wasProcessingFile, setWasProcessingFile] = useState(false);
// Track if we were processing a file (to keep labels after completion)
useEffect(() => {
if (isProcessingFile) {
setWasProcessingFile(true);
}
}, [isProcessingFile]);
useEffect(() => {
// Monitor memory usage if available
if (performance.memory) {
const interval = setInterval(() => {
const memory = performance.memory;
setMemoryUsage({
used: (memory.usedJSHeapSize / 1024 / 1024).toFixed(1),
total: (memory.totalJSHeapSize / 1024 / 1024).toFixed(1),
limit: (memory.jsHeapSizeLimit / 1024 / 1024).toFixed(1),
});
}, 1000);
return () => clearInterval(interval);
}
}, []);
const formatDuration = (seconds) => {
if (seconds === null || seconds === undefined) return null;
if (seconds < 60) {
return seconds.toFixed(1);
}
const minutes = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${minutes}m ${secs}s`;
};
const MetricCard = ({ label, value, unit, color = 'gray', formatTime = false }) => {
const displayValue = formatTime ? formatDuration(value) : (value !== null && value !== undefined ? value.toFixed(2) : null);
const displayUnit = formatTime ? '' : unit;
return (
{label}
{displayValue !== null ? displayValue : 'β'}
{displayUnit && {displayUnit}}
);
};
const getRTFColor = (rtf) => {
if (rtf === null || rtf === undefined) return 'gray';
// Higher RTF is better (means faster than real-time)
if (rtf > 5) return 'green'; // Excellent (5x+ real-time)
if (rtf > 1) return 'yellow'; // Good (faster than real-time)
return 'red'; // Slow (slower than real-time)
};
const getLatencyColor = (latency) => {
if (latency === null || latency === undefined) return 'cyan';
// Lower latency is better
if (latency < 0.5) return 'green'; // Excellent (<500ms)
if (latency < 1.0) return 'yellow'; // Good (<1s)
return 'red'; // Slow (>1s)
};
const getWindowStateIcon = (state) => {
if (state === 'growing') return 'π';
if (state === 'sliding') return 'βοΈ';
return 'βΈοΈ';
};
return (
π Performance Metrics
{/* Metrics Grid */}
{/* Additional Info */}
{/* Window State */}
Window State
{getWindowStateIcon(windowState)} {windowState || 'idle'}
{windowState === 'growing' && 'Building context (0-15s)'}
{windowState === 'sliding' && 'Sliding window (>15s)'}
{!windowState && 'Not recording'}
{/* Device */}
Acceleration
{device === 'webgpu-hybrid' && 'π WebGPU Hybrid'}
{device === 'webgpu' && 'π WebGPU'}
{device === 'wasm' && 'βοΈ WebAssembly'}
{device === 'cpu' && 'π₯οΈ CPU'}
{!device && 'β'}
{device === 'webgpu-hybrid' && 'GPU encoder + WASM decoder'}
{device === 'webgpu' && 'Hardware accelerated'}
{device === 'wasm' && 'Software optimized'}
{device === 'cpu' && 'Fallback mode'}
{/* Memory (if available) */}
{memoryUsage && (
Memory Usage
{memoryUsage.used} MB
of {memoryUsage.total} MB allocated
)}
{/* RTF Explanation */}
{rtf !== null && rtf !== undefined && (
Real-time Factor (RTF): How many times faster than real-time.
{rtf > 1 && ` β ${rtf.toFixed(1)}x faster than real-time`}
{rtf <= 1 && ' β οΈ Slower than real-time'}
{' (Higher is better)'}
)}
{/* Technical Info */}
Model: Parakeet TDT 0.6B v3 (ONNX) | Sample Rate: 16kHz
Progressive updates every 500ms | Smart window management (15s max)
);
}