2025-11-20 21:30:37 +01:00
|
|
|
'use client';
|
|
|
|
|
|
2025-11-21 16:08:24 +01:00
|
|
|
import { useEffect, useRef, useState, useCallback } from 'react';
|
2025-11-20 21:30:37 +01:00
|
|
|
import { useCanvasStore, useLayerStore, useToolStore } from '@/store';
|
|
|
|
|
import { useHistoryStore } from '@/store/history-store';
|
feat(phase-8): implement comprehensive selection system with marching ants
This commit completes Phase 8 of the paint-ui implementation, adding a full
selection system with multiple selection tools, operations, and visual feedback.
**New Files:**
- types/selection.ts: Selection types, masks, and state interfaces
- lib/selection-utils.ts: Selection mask generation and manipulation algorithms
- lib/selection-operations.ts: Copy/cut/paste/delete/fill/stroke operations
- store/selection-store.ts: Selection state management with Zustand
- core/commands/selection-command.ts: Undo/redo commands for selections
- tools/rectangular-selection-tool.ts: Rectangular marquee selection
- tools/elliptical-selection-tool.ts: Elliptical marquee selection
- tools/lasso-selection-tool.ts: Free-form polygon selection
- tools/magic-wand-tool.ts: Color-based flood-fill selection
- components/selection/selection-panel.tsx: Complete selection UI panel
- components/selection/index.ts: Selection components barrel export
**Updated Files:**
- components/canvas/canvas-with-tools.tsx: Added selection tools integration and marching ants animation
- components/editor/editor-layout.tsx: Integrated SelectionPanel into layout
- store/index.ts: Added selection-store export
- store/canvas-store.ts: Renamed Selection to CanvasSelection to avoid conflicts
- tools/index.ts: Added selection tool exports
- types/index.ts: Added selection types export
- types/canvas.ts: Renamed Selection interface to CanvasSelection
**Selection Tools:**
**Marquee Tools:**
- ✨ Rectangular Selection: Click-drag rectangular regions
- ✨ Elliptical Selection: Click-drag elliptical regions
**Free-form Tools:**
- ✨ Lasso Selection: Draw free-form polygon selections
- ✨ Magic Wand: Color-based flood-fill selection with tolerance
**Selection Modes:**
- 🔷 New: Replace existing selection
- ➕ Add: Add to existing selection
- ➖ Subtract: Remove from existing selection
- ⚡ Intersect: Keep only overlapping areas
**Selection Operations:**
- 📋 Copy/Cut/Paste: Standard clipboard operations with selection mask
- 🗑️ Delete: Remove selected pixels
- 🎨 Fill Selection: Fill with current color
- 🖌️ Stroke Selection: Outline selection with current color
- 🔄 Invert Selection: Invert selected/unselected pixels
- ❌ Clear Selection: Deselect all
**Technical Features:**
- Marching ants animation (animated dashed outline at 50ms interval)
- Selection masks using Uint8Array (0-255 values for anti-aliasing)
- Feathering support (0-250px gaussian blur on selection edges)
- Tolerance control for magic wand (0-255 color difference threshold)
- Scanline polygon fill algorithm for lasso tool
- Flood-fill with Set-based visited tracking for magic wand
- Selection bounds calculation for optimized operations
- Keyboard shortcuts (Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+D, Ctrl+Shift+I)
- Undo/redo integration via selection commands
- Non-destructive operations with proper history tracking
**Algorithm Implementations:**
- Rectangular mask: Simple bounds-based pixel marking
- Elliptical mask: Distance formula from ellipse center
- Lasso mask: Scanline polygon fill with edge intersection
- Magic wand: BFS flood-fill with color tolerance matching
- Mask combination: Per-pixel operations (max, subtract, AND)
- Feathering: Separable box blur (horizontal + vertical passes)
- Mask inversion: Per-pixel NOT operation with bounds recalculation
**UI/UX Features:**
- 264px wide selection panel with all tools and operations
- Tool selection with visual feedback (highlighted active tool)
- Selection mode toggles (new/add/subtract/intersect)
- Feather and tolerance sliders with live value display
- Disabled state when no selection exists
- Keyboard shortcut hints next to operations
- Visual marching ants animation (animated dashes)
Build verified: ✓ Compiled successfully in 1302ms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 02:24:12 +01:00
|
|
|
import { useSelectionStore } from '@/store/selection-store';
|
2025-11-21 14:31:47 +01:00
|
|
|
import { useTextStore } from '@/store/text-store';
|
2025-11-21 17:16:06 +01:00
|
|
|
import { useContextMenuStore } from '@/store/context-menu-store';
|
2025-11-21 17:42:36 +01:00
|
|
|
import { useLayerEffectsStore } from '@/store/layer-effects-store';
|
feat(phase-8): implement comprehensive selection system with marching ants
This commit completes Phase 8 of the paint-ui implementation, adding a full
selection system with multiple selection tools, operations, and visual feedback.
**New Files:**
- types/selection.ts: Selection types, masks, and state interfaces
- lib/selection-utils.ts: Selection mask generation and manipulation algorithms
- lib/selection-operations.ts: Copy/cut/paste/delete/fill/stroke operations
- store/selection-store.ts: Selection state management with Zustand
- core/commands/selection-command.ts: Undo/redo commands for selections
- tools/rectangular-selection-tool.ts: Rectangular marquee selection
- tools/elliptical-selection-tool.ts: Elliptical marquee selection
- tools/lasso-selection-tool.ts: Free-form polygon selection
- tools/magic-wand-tool.ts: Color-based flood-fill selection
- components/selection/selection-panel.tsx: Complete selection UI panel
- components/selection/index.ts: Selection components barrel export
**Updated Files:**
- components/canvas/canvas-with-tools.tsx: Added selection tools integration and marching ants animation
- components/editor/editor-layout.tsx: Integrated SelectionPanel into layout
- store/index.ts: Added selection-store export
- store/canvas-store.ts: Renamed Selection to CanvasSelection to avoid conflicts
- tools/index.ts: Added selection tool exports
- types/index.ts: Added selection types export
- types/canvas.ts: Renamed Selection interface to CanvasSelection
**Selection Tools:**
**Marquee Tools:**
- ✨ Rectangular Selection: Click-drag rectangular regions
- ✨ Elliptical Selection: Click-drag elliptical regions
**Free-form Tools:**
- ✨ Lasso Selection: Draw free-form polygon selections
- ✨ Magic Wand: Color-based flood-fill selection with tolerance
**Selection Modes:**
- 🔷 New: Replace existing selection
- ➕ Add: Add to existing selection
- ➖ Subtract: Remove from existing selection
- ⚡ Intersect: Keep only overlapping areas
**Selection Operations:**
- 📋 Copy/Cut/Paste: Standard clipboard operations with selection mask
- 🗑️ Delete: Remove selected pixels
- 🎨 Fill Selection: Fill with current color
- 🖌️ Stroke Selection: Outline selection with current color
- 🔄 Invert Selection: Invert selected/unselected pixels
- ❌ Clear Selection: Deselect all
**Technical Features:**
- Marching ants animation (animated dashed outline at 50ms interval)
- Selection masks using Uint8Array (0-255 values for anti-aliasing)
- Feathering support (0-250px gaussian blur on selection edges)
- Tolerance control for magic wand (0-255 color difference threshold)
- Scanline polygon fill algorithm for lasso tool
- Flood-fill with Set-based visited tracking for magic wand
- Selection bounds calculation for optimized operations
- Keyboard shortcuts (Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+D, Ctrl+Shift+I)
- Undo/redo integration via selection commands
- Non-destructive operations with proper history tracking
**Algorithm Implementations:**
- Rectangular mask: Simple bounds-based pixel marking
- Elliptical mask: Distance formula from ellipse center
- Lasso mask: Scanline polygon fill with edge intersection
- Magic wand: BFS flood-fill with color tolerance matching
- Mask combination: Per-pixel operations (max, subtract, AND)
- Feathering: Separable box blur (horizontal + vertical passes)
- Mask inversion: Per-pixel NOT operation with bounds recalculation
**UI/UX Features:**
- 264px wide selection panel with all tools and operations
- Tool selection with visual feedback (highlighted active tool)
- Selection mode toggles (new/add/subtract/intersect)
- Feather and tolerance sliders with live value display
- Disabled state when no selection exists
- Keyboard shortcut hints next to operations
- Visual marching ants animation (animated dashes)
Build verified: ✓ Compiled successfully in 1302ms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 02:24:12 +01:00
|
|
|
import { drawMarchingAnts } from '@/lib/selection-utils';
|
2025-11-20 21:30:37 +01:00
|
|
|
import { getContext, drawGrid, drawCheckerboard } from '@/lib/canvas-utils';
|
2025-11-21 14:31:47 +01:00
|
|
|
import { renderText } from '@/lib/text-utils';
|
2025-11-21 17:42:36 +01:00
|
|
|
import { applyLayerEffects } from '@/lib/layer-effects-renderer';
|
2025-11-20 21:30:37 +01:00
|
|
|
import { DrawCommand } from '@/core/commands';
|
2025-11-21 16:08:24 +01:00
|
|
|
import { getTool, preloadCommonTools } from '@/lib/tool-loader';
|
2025-11-21 16:48:19 +01:00
|
|
|
import { useTouchGestures } from '@/hooks/use-touch-gestures';
|
2025-11-21 16:08:24 +01:00
|
|
|
import type { BaseTool } from '@/tools';
|
2025-11-20 21:30:37 +01:00
|
|
|
import type { PointerState } from '@/types';
|
|
|
|
|
import { cn } from '@/lib/utils';
|
2025-11-21 14:31:47 +01:00
|
|
|
import { OnCanvasTextEditor } from './on-canvas-text-editor';
|
feat(phase-12): add professional UI polish with status bar, navigator, and shortcuts help
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>
2025-11-21 17:51:32 +01:00
|
|
|
import { MiniMap } from './mini-map';
|
2025-11-21 17:16:06 +01:00
|
|
|
import {
|
|
|
|
|
Scissors,
|
|
|
|
|
Copy,
|
|
|
|
|
Clipboard,
|
|
|
|
|
Undo2,
|
|
|
|
|
Redo2,
|
|
|
|
|
Layers,
|
|
|
|
|
SquareDashedMousePointer,
|
|
|
|
|
RotateCw,
|
|
|
|
|
FlipHorizontal,
|
|
|
|
|
FlipVertical,
|
|
|
|
|
} from 'lucide-react';
|
2025-11-20 21:30:37 +01:00
|
|
|
|
feat(phase-12): add professional UI polish with status bar, navigator, and shortcuts help
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>
2025-11-21 17:51:32 +01:00
|
|
|
interface CanvasWithToolsProps {
|
|
|
|
|
onCursorMove?: (pos: { x: number; y: number } | undefined) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function CanvasWithTools({ onCursorMove }: CanvasWithToolsProps = {}) {
|
2025-11-20 21:30:37 +01:00
|
|
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
|
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
const drawCommandRef = useRef<DrawCommand | null>(null);
|
2025-11-21 16:08:24 +01:00
|
|
|
const toolsCache = useRef<Record<string, BaseTool>>({});
|
2025-11-20 21:30:37 +01:00
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
zoom,
|
|
|
|
|
offsetX,
|
|
|
|
|
offsetY,
|
|
|
|
|
showGrid,
|
|
|
|
|
gridSize,
|
|
|
|
|
backgroundColor,
|
|
|
|
|
selection,
|
|
|
|
|
screenToCanvas,
|
|
|
|
|
} = useCanvasStore();
|
|
|
|
|
|
|
|
|
|
const { layers, getActiveLayer } = useLayerStore();
|
|
|
|
|
const { activeTool, settings } = useToolStore();
|
2025-11-21 17:16:06 +01:00
|
|
|
const { executeCommand, canUndo, canRedo, undo, redo } = useHistoryStore();
|
|
|
|
|
const { activeSelection, selectionType, isMarching, clearSelection, selectAll } = useSelectionStore();
|
2025-11-21 15:10:22 +01:00
|
|
|
const { textObjects, editingTextId, isOnCanvasEditorActive } = useTextStore();
|
2025-11-21 17:16:06 +01:00
|
|
|
const { showContextMenu } = useContextMenuStore();
|
2025-11-21 17:42:36 +01:00
|
|
|
const { getLayerEffects } = useLayerEffectsStore();
|
feat(phase-8): implement comprehensive selection system with marching ants
This commit completes Phase 8 of the paint-ui implementation, adding a full
selection system with multiple selection tools, operations, and visual feedback.
**New Files:**
- types/selection.ts: Selection types, masks, and state interfaces
- lib/selection-utils.ts: Selection mask generation and manipulation algorithms
- lib/selection-operations.ts: Copy/cut/paste/delete/fill/stroke operations
- store/selection-store.ts: Selection state management with Zustand
- core/commands/selection-command.ts: Undo/redo commands for selections
- tools/rectangular-selection-tool.ts: Rectangular marquee selection
- tools/elliptical-selection-tool.ts: Elliptical marquee selection
- tools/lasso-selection-tool.ts: Free-form polygon selection
- tools/magic-wand-tool.ts: Color-based flood-fill selection
- components/selection/selection-panel.tsx: Complete selection UI panel
- components/selection/index.ts: Selection components barrel export
**Updated Files:**
- components/canvas/canvas-with-tools.tsx: Added selection tools integration and marching ants animation
- components/editor/editor-layout.tsx: Integrated SelectionPanel into layout
- store/index.ts: Added selection-store export
- store/canvas-store.ts: Renamed Selection to CanvasSelection to avoid conflicts
- tools/index.ts: Added selection tool exports
- types/index.ts: Added selection types export
- types/canvas.ts: Renamed Selection interface to CanvasSelection
**Selection Tools:**
**Marquee Tools:**
- ✨ Rectangular Selection: Click-drag rectangular regions
- ✨ Elliptical Selection: Click-drag elliptical regions
**Free-form Tools:**
- ✨ Lasso Selection: Draw free-form polygon selections
- ✨ Magic Wand: Color-based flood-fill selection with tolerance
**Selection Modes:**
- 🔷 New: Replace existing selection
- ➕ Add: Add to existing selection
- ➖ Subtract: Remove from existing selection
- ⚡ Intersect: Keep only overlapping areas
**Selection Operations:**
- 📋 Copy/Cut/Paste: Standard clipboard operations with selection mask
- 🗑️ Delete: Remove selected pixels
- 🎨 Fill Selection: Fill with current color
- 🖌️ Stroke Selection: Outline selection with current color
- 🔄 Invert Selection: Invert selected/unselected pixels
- ❌ Clear Selection: Deselect all
**Technical Features:**
- Marching ants animation (animated dashed outline at 50ms interval)
- Selection masks using Uint8Array (0-255 values for anti-aliasing)
- Feathering support (0-250px gaussian blur on selection edges)
- Tolerance control for magic wand (0-255 color difference threshold)
- Scanline polygon fill algorithm for lasso tool
- Flood-fill with Set-based visited tracking for magic wand
- Selection bounds calculation for optimized operations
- Keyboard shortcuts (Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+D, Ctrl+Shift+I)
- Undo/redo integration via selection commands
- Non-destructive operations with proper history tracking
**Algorithm Implementations:**
- Rectangular mask: Simple bounds-based pixel marking
- Elliptical mask: Distance formula from ellipse center
- Lasso mask: Scanline polygon fill with edge intersection
- Magic wand: BFS flood-fill with color tolerance matching
- Mask combination: Per-pixel operations (max, subtract, AND)
- Feathering: Separable box blur (horizontal + vertical passes)
- Mask inversion: Per-pixel NOT operation with bounds recalculation
**UI/UX Features:**
- 264px wide selection panel with all tools and operations
- Tool selection with visual feedback (highlighted active tool)
- Selection mode toggles (new/add/subtract/intersect)
- Feather and tolerance sliders with live value display
- Disabled state when no selection exists
- Keyboard shortcut hints next to operations
- Visual marching ants animation (animated dashes)
Build verified: ✓ Compiled successfully in 1302ms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 02:24:12 +01:00
|
|
|
const [marchingOffset, setMarchingOffset] = useState(0);
|
2025-11-20 21:30:37 +01:00
|
|
|
|
|
|
|
|
const [isPanning, setIsPanning] = useState(false);
|
|
|
|
|
const [panStart, setPanStart] = useState({ x: 0, y: 0 });
|
|
|
|
|
const [pointer, setPointer] = useState<PointerState>({
|
|
|
|
|
isDown: false,
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
prevX: 0,
|
|
|
|
|
prevY: 0,
|
|
|
|
|
pressure: 1,
|
|
|
|
|
});
|
2025-11-21 21:03:28 +01:00
|
|
|
const [cropOverlayNeedsUpdate, setCropOverlayNeedsUpdate] = useState(0);
|
2025-11-20 21:30:37 +01:00
|
|
|
|
2025-11-21 16:48:19 +01:00
|
|
|
// Touch gesture support for mobile
|
|
|
|
|
useTouchGestures(containerRef, {
|
|
|
|
|
minScale: 0.1,
|
|
|
|
|
maxScale: 32,
|
|
|
|
|
});
|
|
|
|
|
|
2025-11-21 16:08:24 +01:00
|
|
|
// Helper to get tool (lazy load if not in cache)
|
|
|
|
|
const getToolInstance = useCallback(async (toolKey: string): Promise<BaseTool | null> => {
|
|
|
|
|
if (toolsCache.current[toolKey]) {
|
|
|
|
|
return toolsCache.current[toolKey];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const tool = await getTool(toolKey as any);
|
|
|
|
|
toolsCache.current[toolKey] = tool;
|
|
|
|
|
return tool;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`Failed to load tool ${toolKey}:`, error);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// Preload common tools on mount
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
preloadCommonTools();
|
|
|
|
|
|
|
|
|
|
// Preload tools into cache
|
|
|
|
|
const initTools = async () => {
|
|
|
|
|
const commonTools = ['pencil', 'brush', 'eraser'];
|
|
|
|
|
for (const toolKey of commonTools) {
|
|
|
|
|
await getToolInstance(toolKey);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
initTools();
|
|
|
|
|
}, [getToolInstance]);
|
|
|
|
|
|
|
|
|
|
// Eagerly load active tool when it changes
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const loadActiveTool = async () => {
|
|
|
|
|
// Load the active tool
|
|
|
|
|
await getToolInstance(activeTool);
|
|
|
|
|
|
|
|
|
|
// For selection tools, also load the specific selection type
|
|
|
|
|
if (activeTool === 'select') {
|
|
|
|
|
await getToolInstance(`${selectionType}-select`);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
loadActiveTool();
|
|
|
|
|
}, [activeTool, selectionType, getToolInstance]);
|
|
|
|
|
|
2025-11-20 21:30:37 +01:00
|
|
|
// Render canvas
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const canvas = canvasRef.current;
|
|
|
|
|
if (!canvas) return;
|
|
|
|
|
|
|
|
|
|
const ctx = getContext(canvas);
|
|
|
|
|
const container = containerRef.current;
|
|
|
|
|
if (!container) return;
|
|
|
|
|
|
|
|
|
|
// Set canvas size to match container
|
|
|
|
|
const rect = container.getBoundingClientRect();
|
|
|
|
|
canvas.width = rect.width;
|
|
|
|
|
canvas.height = rect.height;
|
|
|
|
|
|
|
|
|
|
// Clear canvas
|
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
|
|
|
|
// Save context state
|
|
|
|
|
ctx.save();
|
|
|
|
|
|
|
|
|
|
// Apply transformations
|
|
|
|
|
ctx.translate(offsetX + canvas.width / 2, offsetY + canvas.height / 2);
|
|
|
|
|
ctx.scale(zoom, zoom);
|
|
|
|
|
ctx.translate(-width / 2, -height / 2);
|
|
|
|
|
|
feat: implement UI state persistence and theme toggle
Major improvements to UI state management and user preferences:
- Add theme toggle with dark/light mode support
- Implement Zustand persist middleware for UI state
- Add ui-store for panel layout preferences (dock width, heights, tabs)
- Persist tool settings (active tool, size, opacity, hardness, etc.)
- Persist canvas view preferences (grid, rulers, snap-to-grid)
- Persist shape tool settings
- Persist collapsible section states
- Fix canvas coordinate transformation for centered rendering
- Constrain checkerboard and grid to canvas bounds
- Add icons to all tab buttons and collapsible sections
- Restructure panel-dock to use persisted state
Storage impact: ~3.5KB total across all preferences
Storage keys: tool-storage, canvas-view-storage, shape-storage, ui-storage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 09:03:14 +01:00
|
|
|
// Draw checkerboard background (only within canvas bounds)
|
|
|
|
|
drawCheckerboard(ctx, 10, '#ffffff', '#e0e0e0', width, height);
|
2025-11-20 21:30:37 +01:00
|
|
|
|
|
|
|
|
// Draw background color if not transparent
|
|
|
|
|
if (backgroundColor && backgroundColor !== 'transparent') {
|
|
|
|
|
ctx.fillStyle = backgroundColor;
|
|
|
|
|
ctx.fillRect(0, 0, width, height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw all visible layers
|
|
|
|
|
layers
|
|
|
|
|
.filter((layer) => layer.visible && layer.canvas)
|
|
|
|
|
.sort((a, b) => a.order - b.order)
|
|
|
|
|
.forEach((layer) => {
|
|
|
|
|
if (!layer.canvas) return;
|
|
|
|
|
|
2025-11-21 17:42:36 +01:00
|
|
|
// Get layer effects configuration
|
|
|
|
|
const effectsConfig = getLayerEffects(layer.id);
|
|
|
|
|
|
|
|
|
|
// Apply effects to layer if any exist
|
|
|
|
|
const layerWithEffects = applyLayerEffects(layer.canvas, effectsConfig);
|
|
|
|
|
|
2025-11-20 21:30:37 +01:00
|
|
|
ctx.globalAlpha = layer.opacity;
|
|
|
|
|
ctx.globalCompositeOperation = layer.blendMode as GlobalCompositeOperation;
|
2025-11-21 17:42:36 +01:00
|
|
|
ctx.drawImage(layerWithEffects, layer.x, layer.y);
|
2025-11-20 21:30:37 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Reset composite operation
|
|
|
|
|
ctx.globalAlpha = 1;
|
|
|
|
|
ctx.globalCompositeOperation = 'source-over';
|
|
|
|
|
|
2025-11-21 14:31:47 +01:00
|
|
|
// Draw text objects (skip the one being edited)
|
|
|
|
|
textObjects.forEach((textObj) => {
|
|
|
|
|
// Don't render text that's currently being edited
|
|
|
|
|
if (editingTextId && textObj.id === editingTextId) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderText(ctx, textObj.x, textObj.y, {
|
|
|
|
|
text: textObj.text,
|
|
|
|
|
fontFamily: textObj.fontFamily,
|
|
|
|
|
fontSize: textObj.fontSize,
|
|
|
|
|
fontStyle: textObj.fontStyle,
|
|
|
|
|
fontWeight: textObj.fontWeight,
|
|
|
|
|
color: textObj.color,
|
|
|
|
|
align: textObj.align,
|
|
|
|
|
baseline: textObj.baseline,
|
|
|
|
|
lineHeight: textObj.lineHeight,
|
|
|
|
|
letterSpacing: textObj.letterSpacing,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
feat: implement UI state persistence and theme toggle
Major improvements to UI state management and user preferences:
- Add theme toggle with dark/light mode support
- Implement Zustand persist middleware for UI state
- Add ui-store for panel layout preferences (dock width, heights, tabs)
- Persist tool settings (active tool, size, opacity, hardness, etc.)
- Persist canvas view preferences (grid, rulers, snap-to-grid)
- Persist shape tool settings
- Persist collapsible section states
- Fix canvas coordinate transformation for centered rendering
- Constrain checkerboard and grid to canvas bounds
- Add icons to all tab buttons and collapsible sections
- Restructure panel-dock to use persisted state
Storage impact: ~3.5KB total across all preferences
Storage keys: tool-storage, canvas-view-storage, shape-storage, ui-storage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 09:03:14 +01:00
|
|
|
// Draw grid if enabled (only within canvas bounds)
|
2025-11-20 21:30:37 +01:00
|
|
|
if (showGrid) {
|
feat: implement UI state persistence and theme toggle
Major improvements to UI state management and user preferences:
- Add theme toggle with dark/light mode support
- Implement Zustand persist middleware for UI state
- Add ui-store for panel layout preferences (dock width, heights, tabs)
- Persist tool settings (active tool, size, opacity, hardness, etc.)
- Persist canvas view preferences (grid, rulers, snap-to-grid)
- Persist shape tool settings
- Persist collapsible section states
- Fix canvas coordinate transformation for centered rendering
- Constrain checkerboard and grid to canvas bounds
- Add icons to all tab buttons and collapsible sections
- Restructure panel-dock to use persisted state
Storage impact: ~3.5KB total across all preferences
Storage keys: tool-storage, canvas-view-storage, shape-storage, ui-storage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 09:03:14 +01:00
|
|
|
drawGrid(ctx, gridSize, 'rgba(0, 0, 0, 0.15)', width, height);
|
2025-11-20 21:30:37 +01:00
|
|
|
}
|
|
|
|
|
|
feat(phase-8): implement comprehensive selection system with marching ants
This commit completes Phase 8 of the paint-ui implementation, adding a full
selection system with multiple selection tools, operations, and visual feedback.
**New Files:**
- types/selection.ts: Selection types, masks, and state interfaces
- lib/selection-utils.ts: Selection mask generation and manipulation algorithms
- lib/selection-operations.ts: Copy/cut/paste/delete/fill/stroke operations
- store/selection-store.ts: Selection state management with Zustand
- core/commands/selection-command.ts: Undo/redo commands for selections
- tools/rectangular-selection-tool.ts: Rectangular marquee selection
- tools/elliptical-selection-tool.ts: Elliptical marquee selection
- tools/lasso-selection-tool.ts: Free-form polygon selection
- tools/magic-wand-tool.ts: Color-based flood-fill selection
- components/selection/selection-panel.tsx: Complete selection UI panel
- components/selection/index.ts: Selection components barrel export
**Updated Files:**
- components/canvas/canvas-with-tools.tsx: Added selection tools integration and marching ants animation
- components/editor/editor-layout.tsx: Integrated SelectionPanel into layout
- store/index.ts: Added selection-store export
- store/canvas-store.ts: Renamed Selection to CanvasSelection to avoid conflicts
- tools/index.ts: Added selection tool exports
- types/index.ts: Added selection types export
- types/canvas.ts: Renamed Selection interface to CanvasSelection
**Selection Tools:**
**Marquee Tools:**
- ✨ Rectangular Selection: Click-drag rectangular regions
- ✨ Elliptical Selection: Click-drag elliptical regions
**Free-form Tools:**
- ✨ Lasso Selection: Draw free-form polygon selections
- ✨ Magic Wand: Color-based flood-fill selection with tolerance
**Selection Modes:**
- 🔷 New: Replace existing selection
- ➕ Add: Add to existing selection
- ➖ Subtract: Remove from existing selection
- ⚡ Intersect: Keep only overlapping areas
**Selection Operations:**
- 📋 Copy/Cut/Paste: Standard clipboard operations with selection mask
- 🗑️ Delete: Remove selected pixels
- 🎨 Fill Selection: Fill with current color
- 🖌️ Stroke Selection: Outline selection with current color
- 🔄 Invert Selection: Invert selected/unselected pixels
- ❌ Clear Selection: Deselect all
**Technical Features:**
- Marching ants animation (animated dashed outline at 50ms interval)
- Selection masks using Uint8Array (0-255 values for anti-aliasing)
- Feathering support (0-250px gaussian blur on selection edges)
- Tolerance control for magic wand (0-255 color difference threshold)
- Scanline polygon fill algorithm for lasso tool
- Flood-fill with Set-based visited tracking for magic wand
- Selection bounds calculation for optimized operations
- Keyboard shortcuts (Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+D, Ctrl+Shift+I)
- Undo/redo integration via selection commands
- Non-destructive operations with proper history tracking
**Algorithm Implementations:**
- Rectangular mask: Simple bounds-based pixel marking
- Elliptical mask: Distance formula from ellipse center
- Lasso mask: Scanline polygon fill with edge intersection
- Magic wand: BFS flood-fill with color tolerance matching
- Mask combination: Per-pixel operations (max, subtract, AND)
- Feathering: Separable box blur (horizontal + vertical passes)
- Mask inversion: Per-pixel NOT operation with bounds recalculation
**UI/UX Features:**
- 264px wide selection panel with all tools and operations
- Tool selection with visual feedback (highlighted active tool)
- Selection mode toggles (new/add/subtract/intersect)
- Feather and tolerance sliders with live value display
- Disabled state when no selection exists
- Keyboard shortcut hints next to operations
- Visual marching ants animation (animated dashes)
Build verified: ✓ Compiled successfully in 1302ms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 02:24:12 +01:00
|
|
|
// Draw selection if active (marching ants)
|
|
|
|
|
if (activeSelection && isMarching) {
|
|
|
|
|
drawMarchingAnts(ctx, activeSelection.mask, marchingOffset);
|
2025-11-20 21:30:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restore context state
|
|
|
|
|
ctx.restore();
|
2025-11-21 21:03:28 +01:00
|
|
|
|
|
|
|
|
// Draw crop tool overlay if crop tool is active
|
|
|
|
|
if (activeTool === 'crop') {
|
|
|
|
|
const cropTool = toolsCache.current['crop'];
|
|
|
|
|
if (cropTool && typeof (cropTool as any).drawCropOverlay === 'function') {
|
|
|
|
|
ctx.save();
|
|
|
|
|
ctx.translate(offsetX + canvas.width / 2, offsetY + canvas.height / 2);
|
|
|
|
|
ctx.scale(zoom, zoom);
|
|
|
|
|
ctx.translate(-width / 2, -height / 2);
|
|
|
|
|
|
|
|
|
|
// Create a temporary canvas context that matches the full canvas size
|
|
|
|
|
const tempCanvas = document.createElement('canvas');
|
|
|
|
|
tempCanvas.width = width;
|
|
|
|
|
tempCanvas.height = height;
|
|
|
|
|
const tempCtx = tempCanvas.getContext('2d');
|
|
|
|
|
if (tempCtx) {
|
|
|
|
|
(cropTool as any).drawCropOverlay(tempCtx);
|
|
|
|
|
ctx.drawImage(tempCanvas, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.restore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [layers, width, height, zoom, offsetX, offsetY, showGrid, gridSize, backgroundColor, selection, pointer, activeSelection, isMarching, marchingOffset, textObjects, editingTextId, activeTool, cropOverlayNeedsUpdate]);
|
feat(phase-8): implement comprehensive selection system with marching ants
This commit completes Phase 8 of the paint-ui implementation, adding a full
selection system with multiple selection tools, operations, and visual feedback.
**New Files:**
- types/selection.ts: Selection types, masks, and state interfaces
- lib/selection-utils.ts: Selection mask generation and manipulation algorithms
- lib/selection-operations.ts: Copy/cut/paste/delete/fill/stroke operations
- store/selection-store.ts: Selection state management with Zustand
- core/commands/selection-command.ts: Undo/redo commands for selections
- tools/rectangular-selection-tool.ts: Rectangular marquee selection
- tools/elliptical-selection-tool.ts: Elliptical marquee selection
- tools/lasso-selection-tool.ts: Free-form polygon selection
- tools/magic-wand-tool.ts: Color-based flood-fill selection
- components/selection/selection-panel.tsx: Complete selection UI panel
- components/selection/index.ts: Selection components barrel export
**Updated Files:**
- components/canvas/canvas-with-tools.tsx: Added selection tools integration and marching ants animation
- components/editor/editor-layout.tsx: Integrated SelectionPanel into layout
- store/index.ts: Added selection-store export
- store/canvas-store.ts: Renamed Selection to CanvasSelection to avoid conflicts
- tools/index.ts: Added selection tool exports
- types/index.ts: Added selection types export
- types/canvas.ts: Renamed Selection interface to CanvasSelection
**Selection Tools:**
**Marquee Tools:**
- ✨ Rectangular Selection: Click-drag rectangular regions
- ✨ Elliptical Selection: Click-drag elliptical regions
**Free-form Tools:**
- ✨ Lasso Selection: Draw free-form polygon selections
- ✨ Magic Wand: Color-based flood-fill selection with tolerance
**Selection Modes:**
- 🔷 New: Replace existing selection
- ➕ Add: Add to existing selection
- ➖ Subtract: Remove from existing selection
- ⚡ Intersect: Keep only overlapping areas
**Selection Operations:**
- 📋 Copy/Cut/Paste: Standard clipboard operations with selection mask
- 🗑️ Delete: Remove selected pixels
- 🎨 Fill Selection: Fill with current color
- 🖌️ Stroke Selection: Outline selection with current color
- 🔄 Invert Selection: Invert selected/unselected pixels
- ❌ Clear Selection: Deselect all
**Technical Features:**
- Marching ants animation (animated dashed outline at 50ms interval)
- Selection masks using Uint8Array (0-255 values for anti-aliasing)
- Feathering support (0-250px gaussian blur on selection edges)
- Tolerance control for magic wand (0-255 color difference threshold)
- Scanline polygon fill algorithm for lasso tool
- Flood-fill with Set-based visited tracking for magic wand
- Selection bounds calculation for optimized operations
- Keyboard shortcuts (Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+D, Ctrl+Shift+I)
- Undo/redo integration via selection commands
- Non-destructive operations with proper history tracking
**Algorithm Implementations:**
- Rectangular mask: Simple bounds-based pixel marking
- Elliptical mask: Distance formula from ellipse center
- Lasso mask: Scanline polygon fill with edge intersection
- Magic wand: BFS flood-fill with color tolerance matching
- Mask combination: Per-pixel operations (max, subtract, AND)
- Feathering: Separable box blur (horizontal + vertical passes)
- Mask inversion: Per-pixel NOT operation with bounds recalculation
**UI/UX Features:**
- 264px wide selection panel with all tools and operations
- Tool selection with visual feedback (highlighted active tool)
- Selection mode toggles (new/add/subtract/intersect)
- Feather and tolerance sliders with live value display
- Disabled state when no selection exists
- Keyboard shortcut hints next to operations
- Visual marching ants animation (animated dashes)
Build verified: ✓ Compiled successfully in 1302ms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 02:24:12 +01:00
|
|
|
|
|
|
|
|
// Marching ants animation
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!activeSelection || !isMarching) return;
|
|
|
|
|
|
|
|
|
|
const interval = setInterval(() => {
|
|
|
|
|
setMarchingOffset((prev) => (prev + 1) % 8);
|
|
|
|
|
}, 50);
|
|
|
|
|
|
|
|
|
|
return () => clearInterval(interval);
|
|
|
|
|
}, [activeSelection, isMarching]);
|
2025-11-20 21:30:37 +01:00
|
|
|
|
|
|
|
|
// Handle mouse wheel for zooming
|
|
|
|
|
const handleWheel = (e: React.WheelEvent) => {
|
|
|
|
|
if (e.ctrlKey || e.metaKey) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
const { zoomIn, zoomOut } = useCanvasStore.getState();
|
|
|
|
|
if (e.deltaY < 0) {
|
|
|
|
|
zoomIn();
|
|
|
|
|
} else {
|
|
|
|
|
zoomOut();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Handle pointer down
|
|
|
|
|
const handlePointerDown = (e: React.PointerEvent) => {
|
|
|
|
|
const rect = canvasRef.current?.getBoundingClientRect();
|
|
|
|
|
if (!rect) return;
|
|
|
|
|
|
|
|
|
|
const screenX = e.clientX - rect.left;
|
|
|
|
|
const screenY = e.clientY - rect.top;
|
feat: implement UI state persistence and theme toggle
Major improvements to UI state management and user preferences:
- Add theme toggle with dark/light mode support
- Implement Zustand persist middleware for UI state
- Add ui-store for panel layout preferences (dock width, heights, tabs)
- Persist tool settings (active tool, size, opacity, hardness, etc.)
- Persist canvas view preferences (grid, rulers, snap-to-grid)
- Persist shape tool settings
- Persist collapsible section states
- Fix canvas coordinate transformation for centered rendering
- Constrain checkerboard and grid to canvas bounds
- Add icons to all tab buttons and collapsible sections
- Restructure panel-dock to use persisted state
Storage impact: ~3.5KB total across all preferences
Storage keys: tool-storage, canvas-view-storage, shape-storage, ui-storage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 09:03:14 +01:00
|
|
|
const canvasPos = screenToCanvas(screenX, screenY, rect.width, rect.height);
|
2025-11-20 21:30:37 +01:00
|
|
|
|
|
|
|
|
// Check for panning
|
|
|
|
|
if (e.button === 1 || (e.button === 0 && e.shiftKey)) {
|
|
|
|
|
setIsPanning(true);
|
|
|
|
|
setPanStart({ x: e.clientX - offsetX, y: e.clientY - offsetY });
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 02:36:50 +01:00
|
|
|
// Transform tools
|
|
|
|
|
const transformTools = ['move', 'transform'];
|
|
|
|
|
if (e.button === 0 && !e.shiftKey && transformTools.includes(activeTool)) {
|
2025-11-21 16:08:24 +01:00
|
|
|
const tool = toolsCache.current[activeTool];
|
|
|
|
|
if (!tool) return; // Tool not loaded yet
|
|
|
|
|
|
2025-11-21 02:36:50 +01:00
|
|
|
const newPointer: PointerState = {
|
|
|
|
|
isDown: true,
|
|
|
|
|
x: canvasPos.x,
|
|
|
|
|
y: canvasPos.y,
|
|
|
|
|
prevX: canvasPos.x,
|
|
|
|
|
prevY: canvasPos.y,
|
|
|
|
|
pressure: e.pressure || 1,
|
2025-11-21 16:33:16 +01:00
|
|
|
altKey: e.altKey,
|
|
|
|
|
ctrlKey: e.ctrlKey,
|
|
|
|
|
shiftKey: e.shiftKey,
|
|
|
|
|
metaKey: e.metaKey,
|
2025-11-21 02:36:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setPointer(newPointer);
|
|
|
|
|
tool.onPointerDown(newPointer, {} as any, settings);
|
2025-11-21 09:47:51 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 21:03:28 +01:00
|
|
|
// Crop tool
|
|
|
|
|
if (e.button === 0 && !e.shiftKey && activeTool === 'crop') {
|
|
|
|
|
const cropTool = toolsCache.current['crop'];
|
|
|
|
|
if (!cropTool) return; // Tool not loaded yet
|
|
|
|
|
|
|
|
|
|
const newPointer: PointerState = {
|
|
|
|
|
isDown: true,
|
|
|
|
|
x: canvasPos.x,
|
|
|
|
|
y: canvasPos.y,
|
|
|
|
|
prevX: canvasPos.x,
|
|
|
|
|
prevY: canvasPos.y,
|
|
|
|
|
pressure: e.pressure || 1,
|
|
|
|
|
altKey: e.altKey,
|
|
|
|
|
ctrlKey: e.ctrlKey,
|
|
|
|
|
shiftKey: e.shiftKey,
|
|
|
|
|
metaKey: e.metaKey,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setPointer(newPointer);
|
|
|
|
|
|
|
|
|
|
// Create a temporary canvas for the crop tool
|
|
|
|
|
const tempCanvas = document.createElement('canvas');
|
|
|
|
|
tempCanvas.width = width;
|
|
|
|
|
tempCanvas.height = height;
|
|
|
|
|
const tempCtx = tempCanvas.getContext('2d');
|
|
|
|
|
if (tempCtx) {
|
|
|
|
|
cropTool.onPointerDown(newPointer, tempCtx, settings);
|
|
|
|
|
setCropOverlayNeedsUpdate(prev => prev + 1);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 15:10:22 +01:00
|
|
|
// Text tool - only handle if editor is not already active
|
2025-11-21 09:47:51 +01:00
|
|
|
if (activeTool === 'text') {
|
2025-11-21 15:10:22 +01:00
|
|
|
// If editor is active, let it handle its own events (selection, dragging, click-outside)
|
|
|
|
|
if (isOnCanvasEditorActive) return;
|
|
|
|
|
|
2025-11-21 09:47:51 +01:00
|
|
|
const activeLayer = getActiveLayer();
|
|
|
|
|
if (!activeLayer || !activeLayer.canvas || activeLayer.locked) return;
|
|
|
|
|
|
|
|
|
|
const newPointer: PointerState = {
|
|
|
|
|
isDown: true,
|
|
|
|
|
x: canvasPos.x,
|
|
|
|
|
y: canvasPos.y,
|
|
|
|
|
prevX: canvasPos.x,
|
|
|
|
|
prevY: canvasPos.y,
|
|
|
|
|
pressure: e.pressure || 1,
|
2025-11-21 16:33:16 +01:00
|
|
|
altKey: e.altKey,
|
|
|
|
|
ctrlKey: e.ctrlKey,
|
|
|
|
|
shiftKey: e.shiftKey,
|
|
|
|
|
metaKey: e.metaKey,
|
2025-11-21 09:47:51 +01:00
|
|
|
};
|
|
|
|
|
|
2025-11-21 16:08:24 +01:00
|
|
|
const textTool = toolsCache.current['text'];
|
|
|
|
|
if (textTool) {
|
|
|
|
|
textTool.onPointerDown(newPointer, {} as any, settings);
|
|
|
|
|
}
|
2025-11-21 02:36:50 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
feat(phase-8): implement comprehensive selection system with marching ants
This commit completes Phase 8 of the paint-ui implementation, adding a full
selection system with multiple selection tools, operations, and visual feedback.
**New Files:**
- types/selection.ts: Selection types, masks, and state interfaces
- lib/selection-utils.ts: Selection mask generation and manipulation algorithms
- lib/selection-operations.ts: Copy/cut/paste/delete/fill/stroke operations
- store/selection-store.ts: Selection state management with Zustand
- core/commands/selection-command.ts: Undo/redo commands for selections
- tools/rectangular-selection-tool.ts: Rectangular marquee selection
- tools/elliptical-selection-tool.ts: Elliptical marquee selection
- tools/lasso-selection-tool.ts: Free-form polygon selection
- tools/magic-wand-tool.ts: Color-based flood-fill selection
- components/selection/selection-panel.tsx: Complete selection UI panel
- components/selection/index.ts: Selection components barrel export
**Updated Files:**
- components/canvas/canvas-with-tools.tsx: Added selection tools integration and marching ants animation
- components/editor/editor-layout.tsx: Integrated SelectionPanel into layout
- store/index.ts: Added selection-store export
- store/canvas-store.ts: Renamed Selection to CanvasSelection to avoid conflicts
- tools/index.ts: Added selection tool exports
- types/index.ts: Added selection types export
- types/canvas.ts: Renamed Selection interface to CanvasSelection
**Selection Tools:**
**Marquee Tools:**
- ✨ Rectangular Selection: Click-drag rectangular regions
- ✨ Elliptical Selection: Click-drag elliptical regions
**Free-form Tools:**
- ✨ Lasso Selection: Draw free-form polygon selections
- ✨ Magic Wand: Color-based flood-fill selection with tolerance
**Selection Modes:**
- 🔷 New: Replace existing selection
- ➕ Add: Add to existing selection
- ➖ Subtract: Remove from existing selection
- ⚡ Intersect: Keep only overlapping areas
**Selection Operations:**
- 📋 Copy/Cut/Paste: Standard clipboard operations with selection mask
- 🗑️ Delete: Remove selected pixels
- 🎨 Fill Selection: Fill with current color
- 🖌️ Stroke Selection: Outline selection with current color
- 🔄 Invert Selection: Invert selected/unselected pixels
- ❌ Clear Selection: Deselect all
**Technical Features:**
- Marching ants animation (animated dashed outline at 50ms interval)
- Selection masks using Uint8Array (0-255 values for anti-aliasing)
- Feathering support (0-250px gaussian blur on selection edges)
- Tolerance control for magic wand (0-255 color difference threshold)
- Scanline polygon fill algorithm for lasso tool
- Flood-fill with Set-based visited tracking for magic wand
- Selection bounds calculation for optimized operations
- Keyboard shortcuts (Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+D, Ctrl+Shift+I)
- Undo/redo integration via selection commands
- Non-destructive operations with proper history tracking
**Algorithm Implementations:**
- Rectangular mask: Simple bounds-based pixel marking
- Elliptical mask: Distance formula from ellipse center
- Lasso mask: Scanline polygon fill with edge intersection
- Magic wand: BFS flood-fill with color tolerance matching
- Mask combination: Per-pixel operations (max, subtract, AND)
- Feathering: Separable box blur (horizontal + vertical passes)
- Mask inversion: Per-pixel NOT operation with bounds recalculation
**UI/UX Features:**
- 264px wide selection panel with all tools and operations
- Tool selection with visual feedback (highlighted active tool)
- Selection mode toggles (new/add/subtract/intersect)
- Feather and tolerance sliders with live value display
- Disabled state when no selection exists
- Keyboard shortcut hints next to operations
- Visual marching ants animation (animated dashes)
Build verified: ✓ Compiled successfully in 1302ms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 02:24:12 +01:00
|
|
|
// Selection tools
|
|
|
|
|
const selectionTools = ['select', 'rectangular-select', 'elliptical-select', 'lasso-select', 'magic-wand'];
|
|
|
|
|
if (e.button === 0 && !e.shiftKey && selectionTools.includes(activeTool)) {
|
|
|
|
|
const activeLayer = getActiveLayer();
|
|
|
|
|
if (!activeLayer || !activeLayer.canvas) return;
|
|
|
|
|
|
2025-11-21 16:08:24 +01:00
|
|
|
const tool = toolsCache.current[`${selectionType}-select`] || toolsCache.current['select'];
|
|
|
|
|
if (!tool) return; // Tool not loaded yet
|
feat(phase-8): implement comprehensive selection system with marching ants
This commit completes Phase 8 of the paint-ui implementation, adding a full
selection system with multiple selection tools, operations, and visual feedback.
**New Files:**
- types/selection.ts: Selection types, masks, and state interfaces
- lib/selection-utils.ts: Selection mask generation and manipulation algorithms
- lib/selection-operations.ts: Copy/cut/paste/delete/fill/stroke operations
- store/selection-store.ts: Selection state management with Zustand
- core/commands/selection-command.ts: Undo/redo commands for selections
- tools/rectangular-selection-tool.ts: Rectangular marquee selection
- tools/elliptical-selection-tool.ts: Elliptical marquee selection
- tools/lasso-selection-tool.ts: Free-form polygon selection
- tools/magic-wand-tool.ts: Color-based flood-fill selection
- components/selection/selection-panel.tsx: Complete selection UI panel
- components/selection/index.ts: Selection components barrel export
**Updated Files:**
- components/canvas/canvas-with-tools.tsx: Added selection tools integration and marching ants animation
- components/editor/editor-layout.tsx: Integrated SelectionPanel into layout
- store/index.ts: Added selection-store export
- store/canvas-store.ts: Renamed Selection to CanvasSelection to avoid conflicts
- tools/index.ts: Added selection tool exports
- types/index.ts: Added selection types export
- types/canvas.ts: Renamed Selection interface to CanvasSelection
**Selection Tools:**
**Marquee Tools:**
- ✨ Rectangular Selection: Click-drag rectangular regions
- ✨ Elliptical Selection: Click-drag elliptical regions
**Free-form Tools:**
- ✨ Lasso Selection: Draw free-form polygon selections
- ✨ Magic Wand: Color-based flood-fill selection with tolerance
**Selection Modes:**
- 🔷 New: Replace existing selection
- ➕ Add: Add to existing selection
- ➖ Subtract: Remove from existing selection
- ⚡ Intersect: Keep only overlapping areas
**Selection Operations:**
- 📋 Copy/Cut/Paste: Standard clipboard operations with selection mask
- 🗑️ Delete: Remove selected pixels
- 🎨 Fill Selection: Fill with current color
- 🖌️ Stroke Selection: Outline selection with current color
- 🔄 Invert Selection: Invert selected/unselected pixels
- ❌ Clear Selection: Deselect all
**Technical Features:**
- Marching ants animation (animated dashed outline at 50ms interval)
- Selection masks using Uint8Array (0-255 values for anti-aliasing)
- Feathering support (0-250px gaussian blur on selection edges)
- Tolerance control for magic wand (0-255 color difference threshold)
- Scanline polygon fill algorithm for lasso tool
- Flood-fill with Set-based visited tracking for magic wand
- Selection bounds calculation for optimized operations
- Keyboard shortcuts (Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+D, Ctrl+Shift+I)
- Undo/redo integration via selection commands
- Non-destructive operations with proper history tracking
**Algorithm Implementations:**
- Rectangular mask: Simple bounds-based pixel marking
- Elliptical mask: Distance formula from ellipse center
- Lasso mask: Scanline polygon fill with edge intersection
- Magic wand: BFS flood-fill with color tolerance matching
- Mask combination: Per-pixel operations (max, subtract, AND)
- Feathering: Separable box blur (horizontal + vertical passes)
- Mask inversion: Per-pixel NOT operation with bounds recalculation
**UI/UX Features:**
- 264px wide selection panel with all tools and operations
- Tool selection with visual feedback (highlighted active tool)
- Selection mode toggles (new/add/subtract/intersect)
- Feather and tolerance sliders with live value display
- Disabled state when no selection exists
- Keyboard shortcut hints next to operations
- Visual marching ants animation (animated dashes)
Build verified: ✓ Compiled successfully in 1302ms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 02:24:12 +01:00
|
|
|
const newPointer: PointerState = {
|
|
|
|
|
isDown: true,
|
|
|
|
|
x: canvasPos.x,
|
|
|
|
|
y: canvasPos.y,
|
|
|
|
|
prevX: canvasPos.x,
|
|
|
|
|
prevY: canvasPos.y,
|
|
|
|
|
pressure: e.pressure || 1,
|
2025-11-21 16:33:16 +01:00
|
|
|
altKey: e.altKey,
|
|
|
|
|
ctrlKey: e.ctrlKey,
|
|
|
|
|
shiftKey: e.shiftKey,
|
|
|
|
|
metaKey: e.metaKey,
|
feat(phase-8): implement comprehensive selection system with marching ants
This commit completes Phase 8 of the paint-ui implementation, adding a full
selection system with multiple selection tools, operations, and visual feedback.
**New Files:**
- types/selection.ts: Selection types, masks, and state interfaces
- lib/selection-utils.ts: Selection mask generation and manipulation algorithms
- lib/selection-operations.ts: Copy/cut/paste/delete/fill/stroke operations
- store/selection-store.ts: Selection state management with Zustand
- core/commands/selection-command.ts: Undo/redo commands for selections
- tools/rectangular-selection-tool.ts: Rectangular marquee selection
- tools/elliptical-selection-tool.ts: Elliptical marquee selection
- tools/lasso-selection-tool.ts: Free-form polygon selection
- tools/magic-wand-tool.ts: Color-based flood-fill selection
- components/selection/selection-panel.tsx: Complete selection UI panel
- components/selection/index.ts: Selection components barrel export
**Updated Files:**
- components/canvas/canvas-with-tools.tsx: Added selection tools integration and marching ants animation
- components/editor/editor-layout.tsx: Integrated SelectionPanel into layout
- store/index.ts: Added selection-store export
- store/canvas-store.ts: Renamed Selection to CanvasSelection to avoid conflicts
- tools/index.ts: Added selection tool exports
- types/index.ts: Added selection types export
- types/canvas.ts: Renamed Selection interface to CanvasSelection
**Selection Tools:**
**Marquee Tools:**
- ✨ Rectangular Selection: Click-drag rectangular regions
- ✨ Elliptical Selection: Click-drag elliptical regions
**Free-form Tools:**
- ✨ Lasso Selection: Draw free-form polygon selections
- ✨ Magic Wand: Color-based flood-fill selection with tolerance
**Selection Modes:**
- 🔷 New: Replace existing selection
- ➕ Add: Add to existing selection
- ➖ Subtract: Remove from existing selection
- ⚡ Intersect: Keep only overlapping areas
**Selection Operations:**
- 📋 Copy/Cut/Paste: Standard clipboard operations with selection mask
- 🗑️ Delete: Remove selected pixels
- 🎨 Fill Selection: Fill with current color
- 🖌️ Stroke Selection: Outline selection with current color
- 🔄 Invert Selection: Invert selected/unselected pixels
- ❌ Clear Selection: Deselect all
**Technical Features:**
- Marching ants animation (animated dashed outline at 50ms interval)
- Selection masks using Uint8Array (0-255 values for anti-aliasing)
- Feathering support (0-250px gaussian blur on selection edges)
- Tolerance control for magic wand (0-255 color difference threshold)
- Scanline polygon fill algorithm for lasso tool
- Flood-fill with Set-based visited tracking for magic wand
- Selection bounds calculation for optimized operations
- Keyboard shortcuts (Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+D, Ctrl+Shift+I)
- Undo/redo integration via selection commands
- Non-destructive operations with proper history tracking
**Algorithm Implementations:**
- Rectangular mask: Simple bounds-based pixel marking
- Elliptical mask: Distance formula from ellipse center
- Lasso mask: Scanline polygon fill with edge intersection
- Magic wand: BFS flood-fill with color tolerance matching
- Mask combination: Per-pixel operations (max, subtract, AND)
- Feathering: Separable box blur (horizontal + vertical passes)
- Mask inversion: Per-pixel NOT operation with bounds recalculation
**UI/UX Features:**
- 264px wide selection panel with all tools and operations
- Tool selection with visual feedback (highlighted active tool)
- Selection mode toggles (new/add/subtract/intersect)
- Feather and tolerance sliders with live value display
- Disabled state when no selection exists
- Keyboard shortcut hints next to operations
- Visual marching ants animation (animated dashes)
Build verified: ✓ Compiled successfully in 1302ms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 02:24:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setPointer(newPointer);
|
|
|
|
|
|
|
|
|
|
const ctx = activeLayer.canvas.getContext('2d');
|
|
|
|
|
if (ctx) {
|
|
|
|
|
tool.onPointerDown(newPointer, ctx, settings);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-20 21:30:37 +01:00
|
|
|
// Drawing tools
|
2025-11-21 16:33:16 +01:00
|
|
|
if (e.button === 0 && !e.shiftKey && ['pencil', 'brush', 'eraser', 'fill', 'eyedropper', 'shape', 'clone', 'smudge', 'dodge'].includes(activeTool)) {
|
2025-11-20 21:30:37 +01:00
|
|
|
const activeLayer = getActiveLayer();
|
|
|
|
|
if (!activeLayer || !activeLayer.canvas || activeLayer.locked) return;
|
|
|
|
|
|
|
|
|
|
const newPointer: PointerState = {
|
|
|
|
|
isDown: true,
|
|
|
|
|
x: canvasPos.x,
|
|
|
|
|
y: canvasPos.y,
|
|
|
|
|
prevX: canvasPos.x,
|
|
|
|
|
prevY: canvasPos.y,
|
|
|
|
|
pressure: e.pressure || 1,
|
2025-11-21 16:33:16 +01:00
|
|
|
altKey: e.altKey,
|
|
|
|
|
ctrlKey: e.ctrlKey,
|
|
|
|
|
shiftKey: e.shiftKey,
|
|
|
|
|
metaKey: e.metaKey,
|
2025-11-20 21:30:37 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setPointer(newPointer);
|
|
|
|
|
|
2025-11-21 16:08:24 +01:00
|
|
|
const tool = toolsCache.current[activeTool];
|
|
|
|
|
if (!tool) return; // Tool not loaded yet
|
|
|
|
|
|
2025-11-20 21:30:37 +01:00
|
|
|
// Create draw command for history
|
2025-11-21 16:08:24 +01:00
|
|
|
drawCommandRef.current = new DrawCommand(activeLayer.id, tool.name);
|
2025-11-20 21:30:37 +01:00
|
|
|
|
|
|
|
|
// Call tool's onPointerDown
|
|
|
|
|
const ctx = activeLayer.canvas.getContext('2d');
|
|
|
|
|
if (ctx) {
|
2025-11-21 16:08:24 +01:00
|
|
|
tool.onPointerDown(newPointer, ctx, settings);
|
2025-11-20 21:30:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Handle pointer move
|
|
|
|
|
const handlePointerMove = (e: React.PointerEvent) => {
|
|
|
|
|
const rect = canvasRef.current?.getBoundingClientRect();
|
|
|
|
|
if (!rect) return;
|
|
|
|
|
|
|
|
|
|
const screenX = e.clientX - rect.left;
|
|
|
|
|
const screenY = e.clientY - rect.top;
|
feat: implement UI state persistence and theme toggle
Major improvements to UI state management and user preferences:
- Add theme toggle with dark/light mode support
- Implement Zustand persist middleware for UI state
- Add ui-store for panel layout preferences (dock width, heights, tabs)
- Persist tool settings (active tool, size, opacity, hardness, etc.)
- Persist canvas view preferences (grid, rulers, snap-to-grid)
- Persist shape tool settings
- Persist collapsible section states
- Fix canvas coordinate transformation for centered rendering
- Constrain checkerboard and grid to canvas bounds
- Add icons to all tab buttons and collapsible sections
- Restructure panel-dock to use persisted state
Storage impact: ~3.5KB total across all preferences
Storage keys: tool-storage, canvas-view-storage, shape-storage, ui-storage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 09:03:14 +01:00
|
|
|
const canvasPos = screenToCanvas(screenX, screenY, rect.width, rect.height);
|
2025-11-20 21:30:37 +01:00
|
|
|
|
feat(phase-12): add professional UI polish with status bar, navigator, and shortcuts help
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>
2025-11-21 17:51:32 +01:00
|
|
|
// Report cursor position to parent
|
|
|
|
|
onCursorMove?.(canvasPos);
|
|
|
|
|
|
2025-11-20 21:30:37 +01:00
|
|
|
// Panning
|
|
|
|
|
if (isPanning) {
|
|
|
|
|
const { setPanOffset } = useCanvasStore.getState();
|
|
|
|
|
setPanOffset(e.clientX - panStart.x, e.clientY - panStart.y);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 21:03:28 +01:00
|
|
|
// Crop tool
|
|
|
|
|
if (pointer.isDown && activeTool === 'crop') {
|
|
|
|
|
const cropTool = toolsCache.current['crop'];
|
|
|
|
|
if (!cropTool) return;
|
|
|
|
|
|
|
|
|
|
const newPointer: PointerState = {
|
|
|
|
|
...pointer,
|
|
|
|
|
x: canvasPos.x,
|
|
|
|
|
y: canvasPos.y,
|
|
|
|
|
pressure: e.pressure || 1,
|
|
|
|
|
altKey: e.altKey,
|
|
|
|
|
ctrlKey: e.ctrlKey,
|
|
|
|
|
shiftKey: e.shiftKey,
|
|
|
|
|
metaKey: e.metaKey,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setPointer(newPointer);
|
|
|
|
|
|
|
|
|
|
const tempCanvas = document.createElement('canvas');
|
|
|
|
|
tempCanvas.width = width;
|
|
|
|
|
tempCanvas.height = height;
|
|
|
|
|
const tempCtx = tempCanvas.getContext('2d');
|
|
|
|
|
if (tempCtx) {
|
|
|
|
|
cropTool.onPointerMove(newPointer, tempCtx, settings);
|
|
|
|
|
setCropOverlayNeedsUpdate(prev => prev + 1);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 21:13:01 +01:00
|
|
|
// Selection tools
|
|
|
|
|
const selectionTools = ['select', 'rectangular-select', 'elliptical-select', 'lasso-select', 'magic-wand'];
|
|
|
|
|
if (pointer.isDown && selectionTools.includes(activeTool)) {
|
|
|
|
|
const activeLayer = getActiveLayer();
|
|
|
|
|
if (!activeLayer || !activeLayer.canvas) return;
|
|
|
|
|
|
|
|
|
|
const tool = toolsCache.current[`${selectionType}-select`] || toolsCache.current['select'];
|
|
|
|
|
if (!tool) return;
|
|
|
|
|
|
|
|
|
|
const newPointer: PointerState = {
|
|
|
|
|
...pointer,
|
|
|
|
|
x: canvasPos.x,
|
|
|
|
|
y: canvasPos.y,
|
|
|
|
|
pressure: e.pressure || 1,
|
|
|
|
|
altKey: e.altKey,
|
|
|
|
|
ctrlKey: e.ctrlKey,
|
|
|
|
|
shiftKey: e.shiftKey,
|
|
|
|
|
metaKey: e.metaKey,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setPointer(newPointer);
|
|
|
|
|
|
|
|
|
|
const ctx = activeLayer.canvas.getContext('2d');
|
|
|
|
|
if (ctx) {
|
|
|
|
|
tool.onPointerMove(newPointer, ctx, settings);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-20 21:30:37 +01:00
|
|
|
// Drawing
|
2025-11-21 16:33:16 +01:00
|
|
|
if (pointer.isDown && ['pencil', 'brush', 'eraser', 'eyedropper', 'shape', 'clone', 'smudge', 'dodge'].includes(activeTool)) {
|
2025-11-20 21:30:37 +01:00
|
|
|
const activeLayer = getActiveLayer();
|
|
|
|
|
if (!activeLayer || !activeLayer.canvas) return;
|
|
|
|
|
|
|
|
|
|
const newPointer: PointerState = {
|
|
|
|
|
...pointer,
|
|
|
|
|
x: canvasPos.x,
|
|
|
|
|
y: canvasPos.y,
|
|
|
|
|
pressure: e.pressure || 1,
|
2025-11-21 16:33:16 +01:00
|
|
|
altKey: e.altKey,
|
|
|
|
|
ctrlKey: e.ctrlKey,
|
|
|
|
|
shiftKey: e.shiftKey,
|
|
|
|
|
metaKey: e.metaKey,
|
2025-11-20 21:30:37 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setPointer(newPointer);
|
|
|
|
|
|
2025-11-21 16:08:24 +01:00
|
|
|
const tool = toolsCache.current[activeTool];
|
|
|
|
|
if (!tool) return; // Tool not loaded yet
|
|
|
|
|
|
2025-11-20 21:30:37 +01:00
|
|
|
const ctx = activeLayer.canvas.getContext('2d');
|
|
|
|
|
if (ctx) {
|
2025-11-21 16:08:24 +01:00
|
|
|
tool.onPointerMove(newPointer, ctx, settings);
|
2025-11-20 21:30:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Handle pointer up
|
|
|
|
|
const handlePointerUp = (e: React.PointerEvent) => {
|
|
|
|
|
if (isPanning) {
|
|
|
|
|
setIsPanning(false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 21:03:28 +01:00
|
|
|
// Crop tool
|
|
|
|
|
if (pointer.isDown && activeTool === 'crop') {
|
|
|
|
|
const cropTool = toolsCache.current['crop'];
|
|
|
|
|
if (cropTool) {
|
|
|
|
|
const tempCanvas = document.createElement('canvas');
|
|
|
|
|
tempCanvas.width = width;
|
|
|
|
|
tempCanvas.height = height;
|
|
|
|
|
const tempCtx = tempCanvas.getContext('2d');
|
|
|
|
|
if (tempCtx) {
|
|
|
|
|
cropTool.onPointerUp(pointer, tempCtx, settings);
|
|
|
|
|
setCropOverlayNeedsUpdate(prev => prev + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
setPointer({ ...pointer, isDown: false });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 21:13:01 +01:00
|
|
|
// Selection tools
|
|
|
|
|
const selectionTools = ['select', 'rectangular-select', 'elliptical-select', 'lasso-select', 'magic-wand'];
|
|
|
|
|
if (pointer.isDown && selectionTools.includes(activeTool)) {
|
|
|
|
|
const activeLayer = getActiveLayer();
|
|
|
|
|
if (!activeLayer || !activeLayer.canvas) return;
|
|
|
|
|
|
|
|
|
|
const tool = toolsCache.current[`${selectionType}-select`] || toolsCache.current['select'];
|
|
|
|
|
if (tool) {
|
|
|
|
|
const ctx = activeLayer.canvas.getContext('2d');
|
|
|
|
|
if (ctx) {
|
|
|
|
|
tool.onPointerUp(pointer, ctx, settings);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setPointer({ ...pointer, isDown: false });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pointer.isDown && ['pencil', 'brush', 'eraser', 'fill', 'eyedropper', 'shape', 'clone', 'smudge', 'dodge'].includes(activeTool)) {
|
2025-11-20 21:30:37 +01:00
|
|
|
const activeLayer = getActiveLayer();
|
|
|
|
|
if (!activeLayer || !activeLayer.canvas) return;
|
|
|
|
|
|
2025-11-21 16:08:24 +01:00
|
|
|
const tool = toolsCache.current[activeTool];
|
|
|
|
|
if (!tool) return; // Tool not loaded yet
|
|
|
|
|
|
2025-11-20 21:30:37 +01:00
|
|
|
const ctx = activeLayer.canvas.getContext('2d');
|
|
|
|
|
if (ctx) {
|
2025-11-21 16:08:24 +01:00
|
|
|
tool.onPointerUp(pointer, ctx, settings);
|
2025-11-20 21:30:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Capture after state and add to history
|
|
|
|
|
if (drawCommandRef.current) {
|
|
|
|
|
drawCommandRef.current.captureAfterState();
|
|
|
|
|
executeCommand(drawCommandRef.current);
|
|
|
|
|
drawCommandRef.current = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setPointer({ ...pointer, isDown: false });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
feat(phase-12): add professional UI polish with status bar, navigator, and shortcuts help
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>
2025-11-21 17:51:32 +01:00
|
|
|
// Handle pointer leave
|
|
|
|
|
const handlePointerLeave = (e: React.PointerEvent) => {
|
|
|
|
|
handlePointerUp(e);
|
|
|
|
|
onCursorMove?.(undefined);
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-21 17:16:06 +01:00
|
|
|
// Handle context menu
|
|
|
|
|
const handleContextMenu = (e: React.MouseEvent) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
|
|
const hasSelection = !!activeSelection;
|
|
|
|
|
const activeLayer = getActiveLayer();
|
|
|
|
|
const canMergeDown = activeLayer ? layers.findIndex((l) => l.id === activeLayer.id) < layers.length - 1 : false;
|
|
|
|
|
|
|
|
|
|
showContextMenu(e.clientX, e.clientY, [
|
|
|
|
|
// Clipboard operations
|
|
|
|
|
{
|
|
|
|
|
label: 'Cut',
|
|
|
|
|
icon: <Scissors className="h-4 w-4" />,
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
const { cutSelection } = await import('@/lib/clipboard-operations');
|
|
|
|
|
cutSelection();
|
|
|
|
|
},
|
|
|
|
|
disabled: !hasSelection,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Copy',
|
|
|
|
|
icon: <Copy className="h-4 w-4" />,
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
const { copySelection } = await import('@/lib/clipboard-operations');
|
|
|
|
|
copySelection();
|
|
|
|
|
},
|
|
|
|
|
disabled: !hasSelection,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Paste',
|
|
|
|
|
icon: <Clipboard className="h-4 w-4" />,
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
const { pasteFromClipboard } = await import('@/lib/clipboard-operations');
|
|
|
|
|
await pasteFromClipboard();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
separator: true,
|
|
|
|
|
label: '',
|
|
|
|
|
onClick: () => {},
|
|
|
|
|
},
|
|
|
|
|
// Selection operations
|
|
|
|
|
{
|
|
|
|
|
label: 'Select All',
|
|
|
|
|
icon: <SquareDashedMousePointer className="h-4 w-4" />,
|
|
|
|
|
onClick: () => selectAll(),
|
|
|
|
|
disabled: !activeLayer,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Deselect',
|
|
|
|
|
icon: <SquareDashedMousePointer className="h-4 w-4" />,
|
|
|
|
|
onClick: () => clearSelection(),
|
|
|
|
|
disabled: !hasSelection,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
separator: true,
|
|
|
|
|
label: '',
|
|
|
|
|
onClick: () => {},
|
|
|
|
|
},
|
|
|
|
|
// Layer operations
|
|
|
|
|
{
|
|
|
|
|
label: 'New Layer',
|
|
|
|
|
icon: <Layers className="h-4 w-4" />,
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
const { createLayerWithHistory } = await import('@/lib/layer-operations');
|
|
|
|
|
createLayerWithHistory({
|
|
|
|
|
name: `Layer ${layers.length + 1}`,
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Duplicate Layer',
|
|
|
|
|
icon: <Copy className="h-4 w-4" />,
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
if (!activeLayer) return;
|
|
|
|
|
const { duplicateLayerWithHistory } = await import('@/lib/layer-operations');
|
|
|
|
|
duplicateLayerWithHistory(activeLayer.id);
|
|
|
|
|
},
|
|
|
|
|
disabled: !activeLayer,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Merge Down',
|
|
|
|
|
icon: <Layers className="h-4 w-4" />,
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
if (!activeLayer) return;
|
|
|
|
|
const { mergeLayerDownWithHistory } = await import('@/lib/layer-operations');
|
|
|
|
|
mergeLayerDownWithHistory(activeLayer.id);
|
|
|
|
|
},
|
|
|
|
|
disabled: !canMergeDown,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
separator: true,
|
|
|
|
|
label: '',
|
|
|
|
|
onClick: () => {},
|
|
|
|
|
},
|
|
|
|
|
// Transform operations
|
|
|
|
|
{
|
|
|
|
|
label: 'Rotate 90° CW',
|
|
|
|
|
icon: <RotateCw className="h-4 w-4" />,
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
if (!activeLayer) return;
|
|
|
|
|
const { rotateLayerWithHistory } = await import('@/lib/canvas-operations');
|
|
|
|
|
rotateLayerWithHistory(activeLayer.id, 90);
|
|
|
|
|
},
|
|
|
|
|
disabled: !activeLayer,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Flip Horizontal',
|
|
|
|
|
icon: <FlipHorizontal className="h-4 w-4" />,
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
if (!activeLayer) return;
|
|
|
|
|
const { flipLayerWithHistory } = await import('@/lib/canvas-operations');
|
|
|
|
|
flipLayerWithHistory(activeLayer.id, 'horizontal');
|
|
|
|
|
},
|
|
|
|
|
disabled: !activeLayer,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Flip Vertical',
|
|
|
|
|
icon: <FlipVertical className="h-4 w-4" />,
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
if (!activeLayer) return;
|
|
|
|
|
const { flipLayerWithHistory } = await import('@/lib/canvas-operations');
|
|
|
|
|
flipLayerWithHistory(activeLayer.id, 'vertical');
|
|
|
|
|
},
|
|
|
|
|
disabled: !activeLayer,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
separator: true,
|
|
|
|
|
label: '',
|
|
|
|
|
onClick: () => {},
|
|
|
|
|
},
|
|
|
|
|
// Edit operations
|
|
|
|
|
{
|
|
|
|
|
label: 'Undo',
|
|
|
|
|
icon: <Undo2 className="h-4 w-4" />,
|
|
|
|
|
onClick: () => undo(),
|
|
|
|
|
disabled: !canUndo,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Redo',
|
|
|
|
|
icon: <Redo2 className="h-4 w-4" />,
|
|
|
|
|
onClick: () => redo(),
|
|
|
|
|
disabled: !canRedo,
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-20 21:30:37 +01:00
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
ref={containerRef}
|
|
|
|
|
className={cn(
|
2025-11-21 16:48:19 +01:00
|
|
|
'relative h-full w-full overflow-hidden bg-canvas-bg touch-none',
|
2025-11-21 16:08:24 +01:00
|
|
|
isPanning ? 'cursor-grabbing' : `cursor-${toolsCache.current[activeTool]?.getCursor(settings) || 'default'}`
|
2025-11-20 21:30:37 +01:00
|
|
|
)}
|
2025-11-21 16:48:19 +01:00
|
|
|
style={{ touchAction: 'none' }}
|
2025-11-20 21:30:37 +01:00
|
|
|
onWheel={handleWheel}
|
|
|
|
|
onPointerDown={handlePointerDown}
|
|
|
|
|
onPointerMove={handlePointerMove}
|
|
|
|
|
onPointerUp={handlePointerUp}
|
feat(phase-12): add professional UI polish with status bar, navigator, and shortcuts help
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>
2025-11-21 17:51:32 +01:00
|
|
|
onPointerLeave={handlePointerLeave}
|
2025-11-21 17:16:06 +01:00
|
|
|
onContextMenu={handleContextMenu}
|
2025-11-20 21:30:37 +01:00
|
|
|
>
|
|
|
|
|
<canvas
|
|
|
|
|
ref={canvasRef}
|
|
|
|
|
className="absolute inset-0"
|
|
|
|
|
/>
|
2025-11-21 14:31:47 +01:00
|
|
|
|
|
|
|
|
{/* On-canvas text editor */}
|
|
|
|
|
<OnCanvasTextEditor />
|
feat(phase-12): add professional UI polish with status bar, navigator, and shortcuts help
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>
2025-11-21 17:51:32 +01:00
|
|
|
|
|
|
|
|
{/* Mini-map navigator */}
|
|
|
|
|
<MiniMap />
|
2025-11-20 21:30:37 +01:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|