109 lines
3.2 KiB
TypeScript
109 lines
3.2 KiB
TypeScript
|
|
'use client';
|
|||
|
|
|
|||
|
|
import { useCanvasStore } from '@/store';
|
|||
|
|
import { useState, useEffect } from 'react';
|
|||
|
|
import { Maximize2, ZoomIn, MousePointer, Activity, HardDrive } from 'lucide-react';
|
|||
|
|
|
|||
|
|
interface StatusBarProps {
|
|||
|
|
cursorX?: number;
|
|||
|
|
cursorY?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function StatusBar({ cursorX, cursorY }: StatusBarProps) {
|
|||
|
|
const { width, height, zoom } = useCanvasStore();
|
|||
|
|
const [fps, setFps] = useState(0);
|
|||
|
|
const [memory, setMemory] = useState<number | null>(null);
|
|||
|
|
|
|||
|
|
// FPS counter
|
|||
|
|
useEffect(() => {
|
|||
|
|
let frameCount = 0;
|
|||
|
|
let lastTime = performance.now();
|
|||
|
|
let animationFrameId: number;
|
|||
|
|
|
|||
|
|
const calculateFPS = () => {
|
|||
|
|
frameCount++;
|
|||
|
|
const currentTime = performance.now();
|
|||
|
|
const elapsed = currentTime - lastTime;
|
|||
|
|
|
|||
|
|
if (elapsed >= 1000) {
|
|||
|
|
setFps(Math.round((frameCount * 1000) / elapsed));
|
|||
|
|
frameCount = 0;
|
|||
|
|
lastTime = currentTime;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
animationFrameId = requestAnimationFrame(calculateFPS);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
animationFrameId = requestAnimationFrame(calculateFPS);
|
|||
|
|
|
|||
|
|
return () => {
|
|||
|
|
cancelAnimationFrame(animationFrameId);
|
|||
|
|
};
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
// Memory usage (if available in browser)
|
|||
|
|
useEffect(() => {
|
|||
|
|
const updateMemory = () => {
|
|||
|
|
// @ts-ignore - performance.memory is not in all browsers
|
|||
|
|
if (performance.memory) {
|
|||
|
|
// @ts-ignore
|
|||
|
|
const usedMB = Math.round(performance.memory.usedJSHeapSize / 1024 / 1024);
|
|||
|
|
setMemory(usedMB);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
updateMemory();
|
|||
|
|
const interval = setInterval(updateMemory, 2000); // Update every 2 seconds
|
|||
|
|
|
|||
|
|
return () => clearInterval(interval);
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="flex items-center justify-between h-8 px-4 bg-card border-t border-border text-xs text-muted-foreground">
|
|||
|
|
{/* Left side - Canvas info */}
|
|||
|
|
<div className="flex items-center gap-4">
|
|||
|
|
{/* Canvas dimensions */}
|
|||
|
|
<div className="flex items-center gap-1.5" title="Canvas dimensions">
|
|||
|
|
<Maximize2 className="h-3 w-3" />
|
|||
|
|
<span className="font-mono">
|
|||
|
|
{width} × {height}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Zoom level */}
|
|||
|
|
<div className="flex items-center gap-1.5" title="Zoom level">
|
|||
|
|
<ZoomIn className="h-3 w-3" />
|
|||
|
|
<span className="font-mono">{Math.round(zoom * 100)}%</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Cursor position */}
|
|||
|
|
{cursorX !== undefined && cursorY !== undefined && (
|
|||
|
|
<div className="flex items-center gap-1.5" title="Cursor position">
|
|||
|
|
<MousePointer className="h-3 w-3" />
|
|||
|
|
<span className="font-mono">
|
|||
|
|
X: {Math.round(cursorX)}, Y: {Math.round(cursorY)}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Right side - Performance info */}
|
|||
|
|
<div className="flex items-center gap-4">
|
|||
|
|
{/* FPS */}
|
|||
|
|
<div className="flex items-center gap-1.5" title="Frames per second">
|
|||
|
|
<Activity className="h-3 w-3" />
|
|||
|
|
<span className="font-mono">{fps} FPS</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Memory usage */}
|
|||
|
|
{memory !== null && (
|
|||
|
|
<div className="flex items-center gap-1.5" title="Memory usage">
|
|||
|
|
<HardDrive className="h-3 w-3" />
|
|||
|
|
<span className="font-mono">{memory} MB</span>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|