"use client"; import { useEffect, useState } from "react"; import { formatBytes, formatDuration } from "@/lib/format"; import { KEY_BINDINGS } from "@/lib/pinball/keyBindings"; import type { TableInfo } from "@/lib/pinball/types"; export interface GameInfoPanelProps { tableInfo: TableInfo; loadTimeMs: number | null; } /** Self-contained rAF-based FPS counter — the engine exposes no perf API of its own. */ function useFps(): number | null { const [fps, setFps] = useState(null); useEffect(() => { let frameCount = 0; let windowStart = performance.now(); let raf = 0; const tick = (now: number) => { frameCount += 1; const elapsed = now - windowStart; if (elapsed >= 500) { setFps(Math.round((frameCount * 1000) / elapsed)); frameCount = 0; windowStart = now; } raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, []); return fps; } export default function GameInfoPanel({ tableInfo, loadTimeMs }: GameInfoPanelProps) { const fps = useFps(); return (
TABLE
{tableInfo.name}
FPS
{fps ?? "—"}
Source
{tableInfo.isDemo ? "Bundled demo" : "Your upload"}
Size
{tableInfo.isDemo ? "—" : formatBytes(tableInfo.sizeBytes)}
Load time
{loadTimeMs !== null ? formatDuration(loadTimeMs) : "—"}
ROM
{tableInfo.romFileName ?? "—"}
CONTROLS
    {KEY_BINDINGS.map((binding) => (
  • {binding.action} {binding.keys}
  • ))}
); }