diff --git a/app/accessibility/colorblind/page.tsx b/app/accessibility/colorblind/page.tsx new file mode 100644 index 0000000..7131d3d --- /dev/null +++ b/app/accessibility/colorblind/page.tsx @@ -0,0 +1,214 @@ +'use client'; + +import { useState } from 'react'; +import { ColorPicker } from '@/components/color/ColorPicker'; +import { ColorDisplay } from '@/components/color/ColorDisplay'; +import { Button } from '@/components/ui/button'; +import { Select } from '@/components/ui/select'; +import { useSimulateColorBlindness } from '@/lib/api/queries'; +import { Loader2, Eye, Plus, X } from 'lucide-react'; +import { toast } from 'sonner'; + +type ColorBlindnessType = 'protanopia' | 'deuteranopia' | 'tritanopia'; + +export default function ColorBlindPage() { + const [colors, setColors] = useState(['#ff0099']); + const [blindnessType, setBlindnessType] = useState('protanopia'); + const [simulations, setSimulations] = useState< + Array<{ original: string; simulated: string }> + >([]); + + const simulateMutation = useSimulateColorBlindness(); + + const handleSimulate = async () => { + try { + const result = await simulateMutation.mutateAsync({ + colors, + type: blindnessType, + }); + setSimulations(result.simulations); + toast.success(`Simulated ${blindnessType}`); + } catch (error) { + toast.error('Failed to simulate color blindness'); + console.error(error); + } + }; + + const addColor = () => { + if (colors.length < 10) { + setColors([...colors, '#000000']); + } + }; + + const removeColor = (index: number) => { + if (colors.length > 1) { + setColors(colors.filter((_, i) => i !== index)); + } + }; + + const updateColor = (index: number, color: string) => { + const newColors = [...colors]; + newColors[index] = color; + setColors(newColors); + }; + + const typeDescriptions: Record = { + protanopia: 'Red-blind (affects ~1% of males)', + deuteranopia: 'Green-blind (affects ~1% of males)', + tritanopia: 'Blue-blind (rare, affects ~0.001%)', + }; + + return ( +
+
+
+

Color Blindness Simulator

+

+ Simulate how colors appear with different types of color blindness +

+
+ +
+ {/* Controls */} +
+
+
+

Colors to Test

+ +
+ +
+ {colors.map((color, index) => ( +
+
+ updateColor(index, newColor)} + /> +
+ {colors.length > 1 && ( + + )} +
+ ))} +
+
+ +
+

Blindness Type

+
+ + +

+ {typeDescriptions[blindnessType]} +

+ + +
+
+
+ + {/* Results */} +
+ {simulations.length > 0 ? ( + <> +
+

Simulation Results

+

+ Compare original colors (left) with how they appear to people with{' '} + {blindnessType} (right) +

+ +
+ {simulations.map((sim, index) => ( +
+
+

+ Original +

+
+ + {sim.original} +
+
+ +
+

+ As Seen +

+
+ + {sim.simulated} +
+
+
+ ))} +
+
+ +
+

+ + Accessibility Tip +

+

+ Ensure important information isn't conveyed by color alone. Use text + labels, patterns, or icons to make your design accessible to everyone. +

+
+ + ) : ( +
+ +

Add colors and click Simulate to see how they appear

+

with different types of color blindness

+
+ )} +
+
+
+
+ ); +} diff --git a/app/batch/page.tsx b/app/batch/page.tsx index 427c862..4d0c45d 100644 --- a/app/batch/page.tsx +++ b/app/batch/page.tsx @@ -1,34 +1,188 @@ -import { FileUp } from 'lucide-react'; +'use client'; + +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Select } from '@/components/ui/select'; +import { Input } from '@/components/ui/input'; +import { PaletteGrid } from '@/components/color/PaletteGrid'; +import { ExportMenu } from '@/components/tools/ExportMenu'; +import { useLighten, useDarken, useSaturate, useDesaturate, useRotate } from '@/lib/api/queries'; +import { Loader2, Upload, Download } from 'lucide-react'; +import { toast } from 'sonner'; + +type Operation = 'lighten' | 'darken' | 'saturate' | 'desaturate' | 'rotate'; export default function BatchPage() { + const [inputColors, setInputColors] = useState(''); + const [operation, setOperation] = useState('lighten'); + const [amount, setAmount] = useState(0.2); + const [outputColors, setOutputColors] = useState([]); + + const lightenMutation = useLighten(); + const darkenMutation = useDarken(); + const saturateMutation = useSaturate(); + const desaturateMutation = useDesaturate(); + const rotateMutation = useRotate(); + + const parseColors = (text: string): string[] => { + // Parse colors from text (one per line, or comma-separated) + return text + .split(/[\n,]/) + .map((c) => c.trim()) + .filter((c) => c.length > 0 && c.match(/^#?[0-9a-fA-F]{3,8}$/)); + }; + + const handleProcess = async () => { + const colors = parseColors(inputColors); + + if (colors.length === 0) { + toast.error('No valid colors found'); + return; + } + + if (colors.length > 100) { + toast.error('Maximum 100 colors allowed'); + return; + } + + try { + let result; + + switch (operation) { + case 'lighten': + result = await lightenMutation.mutateAsync({ colors, amount }); + break; + case 'darken': + result = await darkenMutation.mutateAsync({ colors, amount }); + break; + case 'saturate': + result = await saturateMutation.mutateAsync({ colors, amount }); + break; + case 'desaturate': + result = await desaturateMutation.mutateAsync({ colors, amount }); + break; + case 'rotate': + result = await rotateMutation.mutateAsync({ colors, amount: amount * 360 }); + break; + } + + setOutputColors(result.colors); + toast.success(`Processed ${result.colors.length} colors`); + } catch (error) { + toast.error('Failed to process colors'); + console.error(error); + } + }; + + const isPending = + lightenMutation.isPending || + darkenMutation.isPending || + saturateMutation.isPending || + desaturateMutation.isPending || + rotateMutation.isPending; + return (

Batch Operations

- Process multiple colors at once with CSV/JSON upload + Process multiple colors at once with manipulation operations

-
-
- -

Coming Soon

-

- Batch operations will allow you to upload CSV or JSON files with multiple colors - and apply transformations to all of them at once. -

-
-

Planned features:

-
    -
  • • Upload CSV/JSON color lists
  • -
  • • Bulk format conversion
  • -
  • • Apply operations to all colors
  • -
  • • Export results in multiple formats
  • -
  • • Progress tracking for large batches
  • -
+
+ {/* Input */} +
+
+

Input Colors

+

+ Enter colors (one per line or comma-separated). Supports hex format. +

+ +