diff --git a/apps/web/src/components/tools/color-blindness-settings.tsx b/apps/web/src/components/tools/color-blindness-settings.tsx new file mode 100644 index 00000000..8002b72a --- /dev/null +++ b/apps/web/src/components/tools/color-blindness-settings.tsx @@ -0,0 +1,164 @@ +import { Download } from "lucide-react"; +import { useState } from "react"; +import { ProgressCard } from "@/components/common/progress-card"; +import { useToolProcessor } from "@/hooks/use-tool-processor"; +import { useFileStore } from "@/stores/file-store"; + +const SIMULATION_TYPES = [ + { + group: "Red-Green", + types: [ + { + value: "protanopia", + label: "Protanopia", + description: "No red cones. Reds appear dark. ~1% of males.", + }, + { + value: "protanomaly", + label: "Protanomaly", + description: "Reduced red sensitivity. ~1% of males.", + }, + { + value: "deuteranopia", + label: "Deuteranopia", + description: "No green cones. Reds and greens look similar. ~1% of males.", + }, + { + value: "deuteranomaly", + label: "Deuteranomaly", + description: "Reduced green sensitivity. Most common type. ~5% of males.", + }, + ], + }, + { + group: "Blue-Yellow", + types: [ + { + value: "tritanopia", + label: "Tritanopia", + description: "No blue cones. Blues and greens look similar. Very rare.", + }, + { + value: "tritanomaly", + label: "Tritanomaly", + description: "Reduced blue sensitivity. Very rare.", + }, + ], + }, + { + group: "Monochromatic", + types: [ + { + value: "achromatopsia", + label: "Achromatopsia", + description: "Complete color blindness. Sees only luminance. Very rare.", + }, + { + value: "blueConeMonochromacy", + label: "Blue Cone Monochromacy", + description: "Only blue cones functional. Very limited color. Very rare.", + }, + ], + }, +]; + +const TYPE_MAP = new Map(SIMULATION_TYPES.flatMap((g) => g.types.map((t) => [t.value, t]))); + +export function ColorBlindnessSettings() { + const { files } = useFileStore(); + const { + processFiles, + processAllFiles, + processing, + error, + downloadUrl, + originalSize, + processedSize, + progress, + } = useToolProcessor("color-blindness"); + + const [simulationType, setSimulationType] = useState("deuteranomaly"); + const selectedInfo = TYPE_MAP.get(simulationType); + + const handleProcess = () => { + const settings = { simulationType }; + if (files.length > 1) { + processAllFiles(files, settings); + } else { + processFiles(files, settings); + } + }; + + const hasFile = files.length > 0; + + return ( +
+
+ + + {selectedInfo && ( +

{selectedInfo.description}

+ )} +
+ + {error &&

{error}

} + + {originalSize != null && processedSize != null && ( +
+

Original: {(originalSize / 1024).toFixed(1)} KB

+

Processed: {(processedSize / 1024).toFixed(1)} KB

+
+ )} + + {processing ? ( + + ) : ( + + )} + + {downloadUrl && ( + + + Download + + )} +
+ ); +} diff --git a/apps/web/src/lib/tool-registry.tsx b/apps/web/src/lib/tool-registry.tsx index 5962107d..431e8983 100644 --- a/apps/web/src/lib/tool-registry.tsx +++ b/apps/web/src/lib/tool-registry.tsx @@ -308,6 +308,11 @@ const TransparencyFixerSettings = lazy(() => default: m.TransparencyFixerSettings, })), ); +const ColorBlindnessSettings = lazy(() => + import("@/components/tools/color-blindness-settings").then((m) => ({ + default: m.ColorBlindnessSettings, + })), +); // ── Color tool wrapper ───────────────────────────────────────────── // Color tools share a single component but differ by toolId. @@ -426,6 +431,7 @@ export const toolRegistry = new Map([ // Adjustments extra ["replace-color", { displayMode: "before-after", Settings: ReplaceColorSettings }], + ["color-blindness", { displayMode: "before-after", Settings: ColorBlindnessSettings }], // AI Tools ["remove-background", { displayMode: "before-after", Settings: RemoveBgSettings }],