'use client'; import { useState, useEffect, useCallback } from 'react'; import { useLayerStore } from '@/store'; import { useHistoryStore } from '@/store/history-store'; import { applyFilter } from '@/lib/filter-utils'; import { FilterCommand } from '@/core/commands/filter-command'; import type { FilterType, FilterParams } from '@/types/filter'; import { X, RotateCcw } from 'lucide-react'; import { cn } from '@/lib/utils'; interface AdjustmentsDialogProps { isOpen: boolean; onClose: () => void; } const FILTER_CATEGORIES = { 'Color Adjustments': ['brightness', 'contrast', 'hue-saturation'] as FilterType[], 'Filters': ['blur', 'sharpen'] as FilterType[], 'Effects': ['invert', 'grayscale', 'sepia', 'threshold', 'posterize'] as FilterType[], }; const FILTER_NAMES: Record = { brightness: 'Brightness', contrast: 'Contrast', 'hue-saturation': 'Hue/Saturation', blur: 'Blur', sharpen: 'Sharpen', invert: 'Invert Colors', grayscale: 'Grayscale', sepia: 'Sepia', threshold: 'Threshold', posterize: 'Posterize', }; export function AdjustmentsDialog({ isOpen, onClose }: AdjustmentsDialogProps) { const { getActiveLayer } = useLayerStore(); const { executeCommand } = useHistoryStore(); const [selectedFilter, setSelectedFilter] = useState('brightness'); const [params, setParams] = useState({ brightness: 0, contrast: 0, hue: 0, saturation: 0, lightness: 0, radius: 5, amount: 50, threshold: 128, levels: 8, }); const [previewCanvas, setPreviewCanvas] = useState(null); const activeLayer = getActiveLayer(); // Update preview when params change useEffect(() => { if (!isOpen || !activeLayer?.canvas) return; const canvas = document.createElement('canvas'); canvas.width = Math.min(activeLayer.canvas.width, 400); canvas.height = Math.min(activeLayer.canvas.height, 400); const ctx = canvas.getContext('2d'); if (!ctx) return; // Draw scaled layer ctx.drawImage(activeLayer.canvas, 0, 0, canvas.width, canvas.height); // Apply filter to preview const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const filtered = applyFilter(imageData, selectedFilter, params); ctx.putImageData(filtered, 0, 0); setPreviewCanvas(canvas); }, [isOpen, activeLayer, selectedFilter, params]); const handleParamChange = useCallback((key: keyof FilterParams, value: number) => { setParams((prev) => ({ ...prev, [key]: value })); }, []); const handleReset = useCallback(() => { setParams({ brightness: 0, contrast: 0, hue: 0, saturation: 0, lightness: 0, radius: 5, amount: 50, threshold: 128, levels: 8, }); }, []); const handleApply = useCallback(async () => { if (!activeLayer) return; // Use the static method to apply filter and create command const command = await FilterCommand.applyToLayerAsync(activeLayer, selectedFilter, params); executeCommand(command); onClose(); }, [activeLayer, selectedFilter, params, executeCommand, onClose]); if (!isOpen) return null; return ( <> {/* Backdrop */}
{/* Dialog */}
{/* Header */}

Filters & Adjustments

{/* Content */}
{/* Sidebar - Filter List */}
{Object.entries(FILTER_CATEGORIES).map(([category, filters]) => (
{category}
{filters.map((filter) => ( ))}
))}
{/* Main Area */}
{/* Preview */}
{previewCanvas && (
{ if (el && previewCanvas) { el.width = previewCanvas.width; el.height = previewCanvas.height; const ctx = el.getContext('2d'); if (ctx) ctx.drawImage(previewCanvas, 0, 0); } }} className="border border-border rounded shadow-lg" style={{ maxWidth: '400px', maxHeight: '400px' }} />
Preview
)}
{/* Controls */}
{/* Brightness/Contrast */} {selectedFilter === 'brightness' && (
handleParamChange('brightness', Number(e.target.value))} className="w-full" />
)} {selectedFilter === 'contrast' && (
handleParamChange('contrast', Number(e.target.value))} className="w-full" />
)} {selectedFilter === 'hue-saturation' && ( <>
handleParamChange('hue', Number(e.target.value))} className="w-full" />
handleParamChange('saturation', Number(e.target.value))} className="w-full" />
handleParamChange('lightness', Number(e.target.value))} className="w-full" />
)} {selectedFilter === 'blur' && (
handleParamChange('radius', Number(e.target.value))} className="w-full" />
)} {selectedFilter === 'sharpen' && (
handleParamChange('amount', Number(e.target.value))} className="w-full" />
)} {selectedFilter === 'threshold' && (
handleParamChange('threshold', Number(e.target.value))} className="w-full" />
)} {selectedFilter === 'posterize' && (
handleParamChange('levels', Number(e.target.value))} className="w-full" />
)} {/* One-click filters (no params) */} {['invert', 'grayscale', 'sepia'].includes(selectedFilter) && (
This filter has no adjustable parameters. Click Apply to use it.
)}
{/* Footer */}
); }