Download src/components/QueryBench.tsx from jay-hank/Nexova: direct link, hf CLI and curl.
- Browser
- Download file 5.67 kB
-
https://huggingface.co/spaces/jay-hank/Nexova/resolve/main/src/components/QueryBench.tsx
- Command line
-
hf download hf://spaces/jay-hank/Nexova/src/components/QueryBench.tsx
-
curl -L -o QueryBench.tsx https://huggingface.co/spaces/jay-hank/Nexova/resolve/main/src/components/QueryBench.tsx
5.67 kB
| "use client"; | |
| import { useState } from "react"; | |
| import { runBenchmark } from "@/lib/actions"; | |
| type BenchResult = { name: string; ms: number; rows?: number }; | |
| const badge = (ms: number) => | |
| ms < 1 ? "text-emerald-400" : ms < 5 ? "text-yellow-400" : ms < 20 ? "text-orange-400" : "text-red-400"; | |
| const bar = (ms: number, max: number) => | |
| Math.max(4, Math.min(100, (ms / max) * 100)); | |
| export default function QueryBench() { | |
| const [results, setResults] = useState<BenchResult[]>([]); | |
| const [total, setTotal] = useState(0); | |
| const [loading, setLoading] = useState(false); | |
| const [runs, setRuns] = useState<number[]>([]); | |
| const run = async () => { | |
| setLoading(true); | |
| try { | |
| const r = await runBenchmark(); | |
| setResults(r.results); | |
| setTotal(r.totalMs); | |
| setRuns((p) => [...p.slice(-9), r.totalMs]); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const max = results.length ? Math.max(...results.map((r) => r.ms), 0.1) : 1; | |
| const avg = runs.length ? +(runs.reduce((a, b) => a + b, 0) / runs.length).toFixed(2) : 0; | |
| return ( | |
| <div className="space-y-4"> | |
| <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3"> | |
| <div> | |
| <h2 className="text-lg font-bold">⚡ Query Benchmark</h2> | |
| <p className="text-xs text-zinc-500">SQLite query performance — direct server-side via Prisma (no API roundtrip)</p> | |
| </div> | |
| <button | |
| onClick={run} | |
| disabled={loading} | |
| className="bg-emerald-600 hover:bg-emerald-500 disabled:bg-zinc-700 px-5 py-2.5 rounded-lg text-sm font-medium transition shrink-0" | |
| > | |
| {loading ? "Running..." : "🚀 Run Benchmark"} | |
| </button> | |
| </div> | |
| {results.length > 0 && ( | |
| <> | |
| <div className="grid grid-cols-2 sm:grid-cols-4 gap-3"> | |
| <Stat label="Total" value={`${total}ms`} /> | |
| <Stat label="Queries" value={`${results.length}`} /> | |
| <Stat label="Avg/query" value={`${(total / results.length).toFixed(2)}ms`} /> | |
| <Stat label={`Avg (${runs.length} runs)`} value={`${avg}ms`} /> | |
| </div> | |
| <div className="bg-zinc-900 border border-zinc-800 rounded-xl overflow-hidden overflow-x-auto"> | |
| <div className="min-w-[480px]"> | |
| <div className="grid grid-cols-[1fr_80px_60px_1fr] gap-2 px-4 py-2.5 border-b border-zinc-800 text-[11px] text-zinc-500 font-medium uppercase tracking-wider"> | |
| <span>Query</span> | |
| <span className="text-right">Time</span> | |
| <span className="text-right">Rows</span> | |
| <span>Speed</span> | |
| </div> | |
| {results.map((r, i) => ( | |
| <div | |
| key={i} | |
| className="grid grid-cols-[1fr_80px_60px_1fr] gap-2 px-4 py-3 border-b border-zinc-800/50 hover:bg-zinc-800/30 transition text-sm items-center" | |
| > | |
| <span className="font-mono text-xs truncate">{r.name}</span> | |
| <span className={`text-right font-mono text-xs font-bold ${badge(r.ms)}`}> | |
| {r.ms}ms | |
| </span> | |
| <span className="text-right text-xs text-zinc-500"> | |
| {r.rows !== undefined ? r.rows : "—"} | |
| </span> | |
| <div className="h-2 bg-zinc-800 rounded-full overflow-hidden"> | |
| <div | |
| className={`h-full rounded-full transition-all duration-500 ${r.ms < 1 ? "bg-emerald-500" : r.ms < 5 ? "bg-yellow-500" : r.ms < 20 ? "bg-orange-500" : "bg-red-500"}`} | |
| style={{ width: `${bar(r.ms, max)}%` }} | |
| /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="flex flex-wrap gap-3 text-[11px] text-zinc-500"> | |
| <span className="flex items-center gap-1.5"><span className="w-2.5 h-2.5 rounded-full bg-emerald-500" /><1ms</span> | |
| <span className="flex items-center gap-1.5"><span className="w-2.5 h-2.5 rounded-full bg-yellow-500" />1-5ms</span> | |
| <span className="flex items-center gap-1.5"><span className="w-2.5 h-2.5 rounded-full bg-orange-500" />5-20ms</span> | |
| <span className="flex items-center gap-1.5"><span className="w-2.5 h-2.5 rounded-full bg-red-500" />>20ms</span> | |
| </div> | |
| {runs.length > 1 && ( | |
| <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4"> | |
| <p className="text-xs text-zinc-500 mb-2">Run History (total ms)</p> | |
| <div className="flex items-end gap-1 h-16"> | |
| {runs.map((r, i) => { | |
| const h = Math.max(8, (r / Math.max(...runs)) * 100); | |
| return ( | |
| <div key={i} className="flex-1 flex flex-col items-center gap-1"> | |
| <span className="text-[9px] text-zinc-500">{r.toFixed(0)}</span> | |
| <div | |
| className={`w-full rounded-t ${r < 20 ? "bg-emerald-600" : r < 50 ? "bg-yellow-600" : "bg-red-600"}`} | |
| style={{ height: `${h}%` }} | |
| /> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </div> | |
| )} | |
| </> | |
| )} | |
| </div> | |
| ); | |
| } | |
| function Stat({ label, value }: { label: string; value: string }) { | |
| return ( | |
| <div className="bg-zinc-900 border border-zinc-800 rounded-lg p-3 text-center"> | |
| <p className="text-lg font-bold font-mono">{value}</p> | |
| <p className="text-[10px] text-zinc-500 mt-0.5">{label}</p> | |
| </div> | |
| ); | |
| } | |