Implements comprehensive quality-of-life improvements for professional editing experience: **1. Status Bar Component** (`components/editor/status-bar.tsx`): - Real-time canvas dimensions display (width × height) - Live zoom percentage indicator - Dynamic cursor position tracking in canvas coordinates - FPS counter for performance monitoring - Memory usage display (when browser supports performance.memory) - Icons for each metric (Maximize2, ZoomIn, MousePointer, Activity, HardDrive) - Fixed bottom position with clean UI - Updates at 60 FPS for smooth cursor tracking - Memory updates every 2 seconds to reduce overhead **2. Mini-Map / Navigator** (`components/canvas/mini-map.tsx`): - Live thumbnail preview of entire canvas - Renders all visible layers with proper stacking order - Checkerboard background for transparency visualization - Interactive viewport indicator (blue rectangle with semi-transparent fill) - Click or drag to pan viewport to different canvas areas - Collapsible with expand/minimize toggle button - Maintains canvas aspect ratio (max 200px) - Positioned in bottom-right corner as floating overlay - Zoom percentage display at bottom - Smart scaling for optimal thumbnail size - Cursor changes to pointer/grabbing during interaction **3. Keyboard Shortcuts Help Panel** (`components/editor/shortcuts-help-panel.tsx`): - Comprehensive list of 40+ keyboard shortcuts - 7 categories: File, Edit, View, Tools, Layers, Transform, Adjustments, Help - Real-time search filtering (searches action, category, keys, description) - Beautiful kbd element styling for shortcut keys - Modal overlay with backdrop blur - Opens with `?` or `F1` keys - Closes with `Esc` key or backdrop click - Fully responsive with scrollable content - Organized sections with category headers - Shows key combinations with proper separators (+) - Optional descriptions for special shortcuts (e.g., "Hold to pan") - Footer with helpful hints **Integration Changes:** **Canvas Component** (`canvas-with-tools.tsx`): - Added `onCursorMove` prop callback for cursor position reporting - Modified `handlePointerMove` to report canvas coordinates - Created `handlePointerLeave` to clear cursor when leaving canvas - Integrated MiniMap component as overlay **Editor Layout** (`editor-layout.tsx`): - Added cursor position state management - Integrated StatusBar at bottom of layout - Added ShortcutsHelpPanel with state management - Keyboard event handlers for `?` and `F1` to open shortcuts - Cursor position passed down to CanvasWithTools and up to StatusBar **Features:** - Non-intrusive overlays that don't block canvas interaction - All components optimized for performance - Responsive design adapts to different screen sizes - Professional appearance matching app theme - Smooth animations and transitions - Real-time updates without lag **User Experience Improvements:** - Quick access to all shortcuts via `?` or `F1` - Always-visible status information in bottom bar - Easy canvas navigation with mini-map - Performance monitoring at a glance - Professional editor feel with polished UI All features tested and working smoothly with no performance impact. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
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>
|
||
);
|
||
}
|