{ "name": "fps", "type": "registry:ui", "files": [ { "path": "ui/fps.tsx", "type": "registry:ui", "content": "\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { cn } from \"@/lib/utils\";\n\nconst fpsVariants = cva(\n \"z-50 flex shrink-0 items-center gap-2 rounded-sm border bg-background/80 px-3 py-1.5 font-mono text-foreground text-sm backdrop-blur-sm\",\n {\n variants: {\n strategy: {\n fixed: \"fixed\",\n absolute: \"absolute\",\n },\n position: {\n \"top-left\": \"top-4 left-4\",\n \"top-right\": \"top-4 right-4\",\n \"bottom-left\": \"bottom-4 left-4\",\n \"bottom-right\": \"right-4 bottom-4\",\n },\n status: {\n good: \"text-primary\",\n warning: \"text-orange-500\",\n error: \"text-destructive\",\n },\n },\n defaultVariants: {\n strategy: \"fixed\",\n position: \"top-right\",\n status: \"good\",\n },\n },\n);\n\ninterface FpsProps\n extends React.ComponentProps<\"div\">,\n Omit, \"status\"> {\n label?: string;\n updateInterval?: number;\n warningThreshold?: number;\n errorThreshold?: number;\n portalContainer?: Element | DocumentFragment | null;\n enabled?: boolean;\n}\n\nfunction Fps(props: FpsProps) {\n const {\n strategy = \"fixed\",\n position = \"top-right\",\n label,\n updateInterval = 500,\n warningThreshold = 30,\n errorThreshold = 20,\n portalContainer: portalContainerProp,\n enabled = true,\n className,\n ...fpsProps\n } = props;\n\n const [mounted, setMounted] = React.useState(false);\n const [fps, setFps] = React.useState(0);\n const frameCountRef = React.useRef(0);\n const lastTimeRef = React.useRef(performance.now());\n const animationFrameRef = React.useRef(null);\n const updateTimeoutRef = React.useRef | null>(\n null,\n );\n\n React.useLayoutEffect(() => setMounted(true), []);\n\n const status = React.useMemo(() => {\n if (fps < errorThreshold) return \"error\";\n if (fps < warningThreshold) return \"warning\";\n return \"good\";\n }, [fps, errorThreshold, warningThreshold]);\n\n React.useEffect(() => {\n if (!enabled || typeof window === \"undefined\") return;\n\n function measureFps() {\n const now = performance.now();\n const delta = now - lastTimeRef.current;\n frameCountRef.current += 1;\n\n if (delta >= updateInterval) {\n const currentFps = Math.round((frameCountRef.current * 1000) / delta);\n setFps(currentFps);\n frameCountRef.current = 0;\n lastTimeRef.current = now;\n }\n\n animationFrameRef.current = requestAnimationFrame(measureFps);\n }\n\n animationFrameRef.current = requestAnimationFrame(measureFps);\n\n return () => {\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n if (updateTimeoutRef.current !== null) {\n clearTimeout(updateTimeoutRef.current);\n }\n };\n }, [enabled, updateInterval]);\n\n if (!enabled) return null;\n\n const portalContainer =\n strategy === \"absolute\"\n ? null\n : (portalContainerProp ?? (mounted ? globalThis.document?.body : null));\n\n const Comp = (\n \n {label && (\n \n {label}:\n \n )}\n {fps}\n \n );\n\n return portalContainer ? ReactDOM.createPortal(Comp, portalContainer) : Comp;\n}\n\nexport { Fps };\n", "target": "" } ] }