Files
SnapOtter/apps/web/src/lib/tool-registry.tsx
T

1132 lines
42 KiB
TypeScript

/**
* Tool UI registry.
*
* Maps each toolId to its settings component, display mode, and capabilities.
* Adding a new tool means adding one entry here instead of editing a 750-line file.
*/
import { AUDIO_INPUTS, IMAGE_INPUTS, SUBTITLE_INPUTS, VIDEO_INPUTS } from "@snapotter/shared";
import type React from "react";
import { lazy } from "react";
import type { Crop } from "react-image-crop";
import type { BgPreviewState } from "@/components/common/image-viewer";
import type { EraserCanvasRef } from "@/components/tools/eraser-canvas";
import type { PreviewTransform } from "@/components/tools/rotate-settings";
import { TOOL_DISPLAY_MODES } from "./tool-display-modes";
// ── Display modes ──────────────────────────────────────────────────
// The DisplayMode type and per-tool map live in tool-display-modes.ts (a pure
// module with no React imports) so tests can introspect them. Re-exported here
// for existing importers.
export type { DisplayMode } from "./tool-display-modes";
import type { DisplayMode } from "./tool-display-modes";
// ── Crop and eraser prop types ─────────────────────────────────────
export interface CropProps {
cropState: {
crop: Crop;
aspect: number | undefined;
showGrid: boolean;
imgDimensions: { width: number; height: number } | null;
};
onCropChange: (crop: Crop) => void;
onAspectChange: (aspect: number | undefined) => void;
onGridToggle: (show: boolean) => void;
}
export interface EraserProps {
eraserRef: React.RefObject<EraserCanvasRef | null>;
hasStrokes: boolean;
brushSize: number;
onBrushSizeChange: (size: number) => void;
onMaskCenter?: (centerPct: number) => void;
maskedFileCount: number;
}
// ── Registry entry ─────────────────────────────────────────────────
export interface ToolRegistryEntry {
/** The display mode for this tool's image viewer. */
displayMode: DisplayMode;
/** Whether this tool supports live preview transforms (rotate, color). */
livePreview?: boolean;
/** Override the default file-picker accept string (e.g. ".svg,.svgz"). */
accept?: string;
/** The settings component for this tool. */
Settings: React.ComponentType<{
onPreviewTransform?: (t: PreviewTransform) => void;
onPreviewFilter?: (filter: string) => void;
onBgPreview?: (state: BgPreviewState | null) => void;
onImageStyle?: (style: React.CSSProperties | null) => void;
onImageOverlay?: (children: React.ReactNode) => void;
cropProps?: CropProps;
eraserProps?: EraserProps;
}>;
/** Optional panel for tools that render custom content in the main area. */
ResultsPanel?: React.ComponentType;
}
// ── Lazy-loaded settings components ────────────────────────────────
// Using dynamic imports so the bundle only loads what's needed.
const ResizeSettings = lazy(() =>
import("@/components/tools/resize-settings").then((m) => ({ default: m.ResizeSettings })),
);
const ContentAwareResizeSettings = lazy(() =>
import("@/components/tools/content-aware-resize-settings").then((m) => ({
default: m.ContentAwareResizeSettings,
})),
);
const AiCanvasExpandSettings = lazy(() =>
import("@/components/tools/ai-canvas-expand-settings").then((m) => ({
default: m.AiCanvasExpandSettings,
})),
);
const CropSettings = lazy(() =>
import("@/components/tools/crop-settings").then((m) => ({ default: m.CropSettings })),
);
const RotateSettings = lazy(() =>
import("@/components/tools/rotate-settings").then((m) => ({ default: m.RotateSettings })),
);
const ConvertSettings = lazy(() =>
import("@/components/tools/convert-settings").then((m) => ({ default: m.ConvertSettings })),
);
const CompressSettings = lazy(() =>
import("@/components/tools/compress-settings").then((m) => ({ default: m.CompressSettings })),
);
const OptimizeForWebSettings = lazy(() =>
import("@/components/tools/optimize-for-web-settings").then((m) => ({
default: m.OptimizeForWebSettings,
})),
);
const StripMetadataSettings = lazy(() =>
import("@/components/tools/strip-metadata-settings").then((m) => ({
default: m.StripMetadataSettings,
})),
);
const EditMetadataSettings = lazy(() =>
import("@/components/tools/edit-metadata-settings").then((m) => ({
default: m.EditMetadataSettings,
})),
);
const ColorSettings = lazy(() =>
import("@/components/tools/color-settings").then((m) => ({ default: m.ColorSettings })),
);
const SharpeningSettings = lazy(() =>
import("@/components/tools/sharpening-settings").then((m) => ({
default: m.SharpeningSettings,
})),
);
const WatermarkTextSettings = lazy(() =>
import("@/components/tools/watermark-text-settings").then((m) => ({
default: m.WatermarkTextSettings,
})),
);
const WatermarkImageSettings = lazy(() =>
import("@/components/tools/watermark-image-settings").then((m) => ({
default: m.WatermarkImageSettings,
})),
);
const TextOverlaySettings = lazy(() =>
import("@/components/tools/text-overlay-settings").then((m) => ({
default: m.TextOverlaySettings,
})),
);
const ComposeSettings = lazy(() =>
import("@/components/tools/compose-settings").then((m) => ({ default: m.ComposeSettings })),
);
const InfoSettings = lazy(() =>
import("@/components/tools/info-settings").then((m) => ({ default: m.InfoSettings })),
);
const CompareSettings = lazy(() =>
import("@/components/tools/compare-settings").then((m) => ({ default: m.CompareSettings })),
);
const FindDuplicatesSettings = lazy(() =>
import("@/components/tools/find-duplicates-settings").then((m) => ({
default: m.FindDuplicatesSettings,
})),
);
const FindDuplicatesResults = lazy(() =>
import("@/components/tools/find-duplicates-results").then((m) => ({
default: m.FindDuplicatesResults,
})),
);
const ColorPaletteSettings = lazy(() =>
import("@/components/tools/color-palette-settings").then((m) => ({
default: m.ColorPaletteSettings,
})),
);
const QrGenerateSettings = lazy(() =>
import("@/components/tools/qr-generate-settings").then((m) => ({
default: m.QrGenerateSettings,
})),
);
const QrGeneratePreview = lazy(() =>
import("@/components/tools/qr-generate-preview").then((m) => ({
default: m.QrGeneratePreview,
})),
);
const HtmlToImageSettings = lazy(() =>
import("@/components/tools/html-to-image-settings").then((m) => ({
default: m.HtmlToImageSettings,
})),
);
const HtmlToImageResults = lazy(() =>
import("@/components/tools/html-to-image-results").then((m) => ({
default: m.HtmlToImageResults,
})),
);
const BarcodeReadSettings = lazy(() =>
import("@/components/tools/barcode-read-settings").then((m) => ({
default: m.BarcodeReadSettings,
})),
);
const ImageToBase64Settings = lazy(() =>
import("@/components/tools/image-to-base64-settings").then((m) => ({
default: m.ImageToBase64Settings,
})),
);
const ImageToBase64Results = lazy(() =>
import("@/components/tools/image-to-base64-results").then((m) => ({
default: m.ImageToBase64Results,
})),
);
const CollageSettings = lazy(() =>
import("@/components/tools/collage-settings").then((m) => ({ default: m.CollageSettings })),
);
const CollagePreview = lazy(() =>
import("@/components/tools/collage-preview").then((m) => ({ default: m.CollagePreview })),
);
const StitchSettings = lazy(() =>
import("@/components/tools/stitch-settings").then((m) => ({ default: m.StitchSettings })),
);
const SplitSettings = lazy(() =>
import("@/components/tools/split-settings").then((m) => ({ default: m.SplitSettings })),
);
const SplitCanvas = lazy(() =>
import("@/components/tools/split-canvas").then((m) => ({ default: m.SplitCanvas })),
);
const BorderSettings = lazy(() =>
import("@/components/tools/border-settings").then((m) => ({ default: m.BorderSettings })),
);
const BeautifySettings = lazy(() =>
import("@/components/tools/beautify-settings").then((m) => ({ default: m.BeautifySettings })),
);
const SvgToRasterSettings = lazy(() =>
import("@/components/tools/svg-to-raster-settings").then((m) => ({
default: m.SvgToRasterSettings,
})),
);
const VectorizeSettings = lazy(() =>
import("@/components/tools/vectorize-settings").then((m) => ({
default: m.VectorizeSettings,
})),
);
const GifToolsSettings = lazy(() =>
import("@/components/tools/gif-tools-settings").then((m) => ({
default: m.GifToolsSettings,
})),
);
const BulkRenameSettings = lazy(() =>
import("@/components/tools/bulk-rename-settings").then((m) => ({
default: m.BulkRenameSettings,
})),
);
const FaviconSettings = lazy(() =>
import("@/components/tools/favicon-settings").then((m) => ({ default: m.FaviconSettings })),
);
const ImageToPdfSettings = lazy(() =>
import("@/components/tools/image-to-pdf-settings").then((m) => ({
default: m.ImageToPdfSettings,
})),
);
const PdfToImageSettings = lazy(() =>
import("@/components/tools/pdf-to-image-settings").then((m) => ({
default: m.PdfToImageSettings,
})),
);
const PdfToImagePreview = lazy(() =>
import("@/components/tools/pdf-to-image-preview").then((m) => ({
default: m.PdfToImagePreview,
})),
);
const ReplaceColorSettings = lazy(() =>
import("@/components/tools/replace-color-settings").then((m) => ({
default: m.ReplaceColorSettings,
})),
);
const RemoveBgSettings = lazy(() =>
import("@/components/tools/remove-bg-settings").then((m) => ({
default: m.RemoveBgSettings,
})),
);
const UpscaleSettings = lazy(() =>
import("@/components/tools/upscale-settings").then((m) => ({ default: m.UpscaleSettings })),
);
const OcrSettings = lazy(() =>
import("@/components/tools/ocr-settings").then((m) => ({ default: m.OcrSettings })),
);
const OcrPdfSettings = lazy(() =>
import("@/components/tools/ocr-pdf-settings").then((m) => ({ default: m.OcrPdfSettings })),
);
const BlurFacesSettings = lazy(() =>
import("@/components/tools/blur-faces-settings").then((m) => ({
default: m.BlurFacesSettings,
})),
);
const EnhanceFacesSettings = lazy(() =>
import("@/components/tools/enhance-faces-settings").then((m) => ({
default: m.EnhanceFacesSettings,
})),
);
const EraseObjectSettings = lazy(() =>
import("@/components/tools/erase-object-settings").then((m) => ({
default: m.EraseObjectSettings,
})),
);
const SmartCropSettings = lazy(() =>
import("@/components/tools/smart-crop-settings").then((m) => ({
default: m.SmartCropSettings,
})),
);
const ImageEnhancementSettings = lazy(() =>
import("@/components/tools/image-enhancement-settings").then((m) => ({
default: m.ImageEnhancementSettings,
})),
);
const ColorizeSettings = lazy(() =>
import("@/components/tools/colorize-settings").then((m) => ({
default: m.ColorizeSettings,
})),
);
const NoiseRemovalSettings = lazy(() =>
import("@/components/tools/noise-removal-settings").then((m) => ({
default: m.NoiseRemovalSettings,
})),
);
const PassportPhotoSettings = lazy(() =>
import("@/components/tools/passport-photo-settings").then((m) => ({
default: m.PassportPhotoSettings,
})),
);
const PassportPhotoPreview = lazy(() =>
import("@/components/tools/passport-photo-settings").then((m) => ({
default: m.PassportPhotoPreview,
})),
);
const RedEyeRemovalSettings = lazy(() =>
import("@/components/tools/red-eye-removal-settings").then((m) => ({
default: m.RedEyeRemovalSettings,
})),
);
const RestorePhotoSettings = lazy(() =>
import("@/components/tools/restore-photo-settings").then((m) => ({
default: m.RestorePhotoSettings,
})),
);
const TransparencyFixerSettings = lazy(() =>
import("@/components/tools/transparency-fixer-settings").then((m) => ({
default: m.TransparencyFixerSettings,
})),
);
const MemeGeneratorSettings = lazy(() =>
import("@/components/tools/meme-generator-settings").then((m) => ({
default: m.MemeGeneratorSettings,
})),
);
const MemeGeneratorPreview = lazy(() =>
import("@/components/tools/meme-generator-preview").then((m) => ({
default: m.MemeGeneratorPreview,
})),
);
const ColorBlindnessSettings = lazy(() =>
import("@/components/tools/color-blindness-settings").then((m) => ({
default: m.ColorBlindnessSettings,
})),
);
const TranscribeAudioSettings = lazy(() =>
import("@/components/tools/transcribe-audio-settings").then((m) => ({
default: m.TranscribeAudioSettings,
})),
);
const AutoSubtitlesSettings = lazy(() =>
import("@/components/tools/auto-subtitles-settings").then((m) => ({
default: m.AutoSubtitlesSettings,
})),
);
const BackgroundReplaceSettings = lazy(() =>
import("@/components/tools/background-replace-settings").then((m) => ({
default: m.BackgroundReplaceSettings,
})),
);
const BlurBackgroundSettings = lazy(() =>
import("@/components/tools/blur-background-settings").then((m) => ({
default: m.BlurBackgroundSettings,
})),
);
const ConvertVideoSettings = lazy(() =>
import("@/components/tools/convert-video-settings").then((m) => ({
default: m.ConvertVideoSettings,
})),
);
const CompressVideoSettings = lazy(() =>
import("@/components/tools/compress-video-settings").then((m) => ({
default: m.CompressVideoSettings,
})),
);
const TrimVideoSettings = lazy(() =>
import("@/components/tools/trim-video-settings").then((m) => ({
default: m.TrimVideoSettings,
})),
);
const MuteVideoSettings = lazy(() =>
import("@/components/tools/mute-video-settings").then((m) => ({
default: m.MuteVideoSettings,
})),
);
const VideoToGifSettings = lazy(() =>
import("@/components/tools/video-to-gif-settings").then((m) => ({
default: m.VideoToGifSettings,
})),
);
const ResizeVideoSettings = lazy(() =>
import("@/components/tools/resize-video-settings").then((m) => ({
default: m.ResizeVideoSettings,
})),
);
const CropVideoSettings = lazy(() =>
import("@/components/tools/crop-video-settings").then((m) => ({
default: m.CropVideoSettings,
})),
);
const RotateVideoSettings = lazy(() =>
import("@/components/tools/rotate-video-settings").then((m) => ({
default: m.RotateVideoSettings,
})),
);
const ChangeFpsSettings = lazy(() =>
import("@/components/tools/change-fps-settings").then((m) => ({
default: m.ChangeFpsSettings,
})),
);
const VideoColorSettings = lazy(() =>
import("@/components/tools/video-color-settings").then((m) => ({
default: m.VideoColorSettings,
})),
);
const VideoSpeedSettings = lazy(() =>
import("@/components/tools/video-speed-settings").then((m) => ({
default: m.VideoSpeedSettings,
})),
);
const ReverseVideoSettings = lazy(() =>
import("@/components/tools/reverse-video-settings").then((m) => ({
default: m.ReverseVideoSettings,
})),
);
const VideoLoudnormSettings = lazy(() =>
import("@/components/tools/video-loudnorm-settings").then((m) => ({
default: m.VideoLoudnormSettings,
})),
);
const AspectPadSettings = lazy(() =>
import("@/components/tools/aspect-pad-settings").then((m) => ({
default: m.AspectPadSettings,
})),
);
const BlurPadSettings = lazy(() =>
import("@/components/tools/blur-pad-settings").then((m) => ({
default: m.BlurPadSettings,
})),
);
const WatermarkVideoSettings = lazy(() =>
import("@/components/tools/watermark-video-settings").then((m) => ({
default: m.WatermarkVideoSettings,
})),
);
const StabilizeVideoSettings = lazy(() =>
import("@/components/tools/stabilize-video-settings").then((m) => ({
default: m.StabilizeVideoSettings,
})),
);
const GifToVideoSettings = lazy(() =>
import("@/components/tools/gif-to-video-settings").then((m) => ({
default: m.GifToVideoSettings,
})),
);
const VideoToWebpSettings = lazy(() =>
import("@/components/tools/video-to-webp-settings").then((m) => ({
default: m.VideoToWebpSettings,
})),
);
const VideoToFramesSettings = lazy(() =>
import("@/components/tools/video-to-frames-settings").then((m) => ({
default: m.VideoToFramesSettings,
})),
);
const MergeVideosSettings = lazy(() =>
import("@/components/tools/merge-videos-settings").then((m) => ({
default: m.MergeVideosSettings,
})),
);
const ReplaceAudioSettings = lazy(() =>
import("@/components/tools/replace-audio-settings").then((m) => ({
default: m.ReplaceAudioSettings,
})),
);
const BurnSubtitlesSettings = lazy(() =>
import("@/components/tools/burn-subtitles-settings").then((m) => ({
default: m.BurnSubtitlesSettings,
})),
);
const EmbedSubtitlesSettings = lazy(() =>
import("@/components/tools/embed-subtitles-settings").then((m) => ({
default: m.EmbedSubtitlesSettings,
})),
);
const ExtractSubtitlesSettings = lazy(() =>
import("@/components/tools/extract-subtitles-settings").then((m) => ({
default: m.ExtractSubtitlesSettings,
})),
);
const ImagesToVideoSettings = lazy(() =>
import("@/components/tools/images-to-video-settings").then((m) => ({
default: m.ImagesToVideoSettings,
})),
);
const VideoMetadataSettings = lazy(() =>
import("@/components/tools/video-metadata-settings").then((m) => ({
default: m.VideoMetadataSettings,
})),
);
const ConvertAudioSettings = lazy(() =>
import("@/components/tools/convert-audio-settings").then((m) => ({
default: m.ConvertAudioSettings,
})),
);
const TrimAudioSettings = lazy(() =>
import("@/components/tools/trim-audio-settings").then((m) => ({
default: m.TrimAudioSettings,
})),
);
const ExtractAudioSettings = lazy(() =>
import("@/components/tools/extract-audio-settings").then((m) => ({
default: m.ExtractAudioSettings,
})),
);
const AudioChannelsSettings = lazy(() =>
import("@/components/tools/audio-channels-settings").then((m) => ({
default: m.AudioChannelsSettings,
})),
);
const AudioMetadataSettings = lazy(() =>
import("@/components/tools/audio-metadata-settings").then((m) => ({
default: m.AudioMetadataSettings,
})),
);
const AudioSpeedSettings = lazy(() =>
import("@/components/tools/audio-speed-settings").then((m) => ({
default: m.AudioSpeedSettings,
})),
);
const FadeAudioSettings = lazy(() =>
import("@/components/tools/fade-audio-settings").then((m) => ({
default: m.FadeAudioSettings,
})),
);
const MergeAudioSettings = lazy(() =>
import("@/components/tools/merge-audio-settings").then((m) => ({
default: m.MergeAudioSettings,
})),
);
const NoiseReductionSettings = lazy(() =>
import("@/components/tools/noise-reduction-settings").then((m) => ({
default: m.NoiseReductionSettings,
})),
);
const NormalizeAudioSettings = lazy(() =>
import("@/components/tools/normalize-audio-settings").then((m) => ({
default: m.NormalizeAudioSettings,
})),
);
const PitchShiftSettings = lazy(() =>
import("@/components/tools/pitch-shift-settings").then((m) => ({
default: m.PitchShiftSettings,
})),
);
const ReverseAudioSettings = lazy(() =>
import("@/components/tools/reverse-audio-settings").then((m) => ({
default: m.ReverseAudioSettings,
})),
);
const RingtoneMakerSettings = lazy(() =>
import("@/components/tools/ringtone-maker-settings").then((m) => ({
default: m.RingtoneMakerSettings,
})),
);
const SilenceRemovalSettings = lazy(() =>
import("@/components/tools/silence-removal-settings").then((m) => ({
default: m.SilenceRemovalSettings,
})),
);
const SplitAudioSettings = lazy(() =>
import("@/components/tools/split-audio-settings").then((m) => ({
default: m.SplitAudioSettings,
})),
);
const VolumeAdjustSettings = lazy(() =>
import("@/components/tools/volume-adjust-settings").then((m) => ({
default: m.VolumeAdjustSettings,
})),
);
const WaveformImageSettings = lazy(() =>
import("@/components/tools/waveform-image-settings").then((m) => ({
default: m.WaveformImageSettings,
})),
);
const MergePdfSettings = lazy(() =>
import("@/components/tools/merge-pdf-settings").then((m) => ({
default: m.MergePdfSettings,
})),
);
const SplitPdfSettings = lazy(() =>
import("@/components/tools/split-pdf-settings").then((m) => ({
default: m.SplitPdfSettings,
})),
);
const CompressPdfSettings = lazy(() =>
import("@/components/tools/compress-pdf-settings").then((m) => ({
default: m.CompressPdfSettings,
})),
);
const RotatePdfSettings = lazy(() =>
import("@/components/tools/rotate-pdf-settings").then((m) => ({
default: m.RotatePdfSettings,
})),
);
const WordToPdfSettings = lazy(() =>
import("@/components/tools/word-to-pdf-settings").then((m) => ({
default: m.WordToPdfSettings,
})),
);
const CsvExcelSettings = lazy(() =>
import("@/components/tools/csv-excel-settings").then((m) => ({
default: m.CsvExcelSettings,
})),
);
const CsvJsonSettings = lazy(() =>
import("@/components/tools/csv-json-settings").then((m) => ({
default: m.CsvJsonSettings,
})),
);
const JsonXmlSettings = lazy(() =>
import("@/components/tools/json-xml-settings").then((m) => ({
default: m.JsonXmlSettings,
})),
);
const SplitCsvSettings = lazy(() =>
import("@/components/tools/split-csv-settings").then((m) => ({
default: m.SplitCsvSettings,
})),
);
const ExtractPagesSettings = lazy(() =>
import("@/components/tools/extract-pages-settings").then((m) => ({
default: m.ExtractPagesSettings,
})),
);
const RemovePagesSettings = lazy(() =>
import("@/components/tools/remove-pages-settings").then((m) => ({
default: m.RemovePagesSettings,
})),
);
const OrganizePdfSettings = lazy(() =>
import("@/components/tools/organize-pdf-settings").then((m) => ({
default: m.OrganizePdfSettings,
})),
);
const ProtectPdfSettings = lazy(() =>
import("@/components/tools/protect-pdf-settings").then((m) => ({
default: m.ProtectPdfSettings,
})),
);
const UnlockPdfSettings = lazy(() =>
import("@/components/tools/unlock-pdf-settings").then((m) => ({
default: m.UnlockPdfSettings,
})),
);
const RepairPdfSettings = lazy(() =>
import("@/components/tools/repair-pdf-settings").then((m) => ({
default: m.RepairPdfSettings,
})),
);
const CropPdfSettings = lazy(() =>
import("@/components/tools/crop-pdf-settings").then((m) => ({
default: m.CropPdfSettings,
})),
);
const NupPdfSettings = lazy(() =>
import("@/components/tools/nup-pdf-settings").then((m) => ({
default: m.NupPdfSettings,
})),
);
const BookletPdfSettings = lazy(() =>
import("@/components/tools/booklet-pdf-settings").then((m) => ({
default: m.BookletPdfSettings,
})),
);
const WatermarkPdfSettings = lazy(() =>
import("@/components/tools/watermark-pdf-settings").then((m) => ({
default: m.WatermarkPdfSettings,
})),
);
const PdfPageNumbersSettings = lazy(() =>
import("@/components/tools/pdf-page-numbers-settings").then((m) => ({
default: m.PdfPageNumbersSettings,
})),
);
const LinearizePdfSettings = lazy(() =>
import("@/components/tools/linearize-pdf-settings").then((m) => ({
default: m.LinearizePdfSettings,
})),
);
const GrayscalePdfSettings = lazy(() =>
import("@/components/tools/grayscale-pdf-settings").then((m) => ({
default: m.GrayscalePdfSettings,
})),
);
const PdfaConvertSettings = lazy(() =>
import("@/components/tools/pdfa-convert-settings").then((m) => ({
default: m.PdfaConvertSettings,
})),
);
const FlattenPdfSettings = lazy(() =>
import("@/components/tools/flatten-pdf-settings").then((m) => ({
default: m.FlattenPdfSettings,
})),
);
const RedactPdfSettings = lazy(() =>
import("@/components/tools/redact-pdf-settings").then((m) => ({
default: m.RedactPdfSettings,
})),
);
const PdfToTextSettings = lazy(() =>
import("@/components/tools/pdf-to-text-settings").then((m) => ({
default: m.PdfToTextSettings,
})),
);
const PdfToWordSettings = lazy(() =>
import("@/components/tools/pdf-to-word-settings").then((m) => ({
default: m.PdfToWordSettings,
})),
);
const PdfMetadataSettings = lazy(() =>
import("@/components/tools/pdf-metadata-settings").then((m) => ({
default: m.PdfMetadataSettings,
})),
);
const HtmlToPdfSettings = lazy(() =>
import("@/components/tools/html-to-pdf-settings").then((m) => ({
default: m.HtmlToPdfSettings,
})),
);
const MarkdownToPdfSettings = lazy(() =>
import("@/components/tools/markdown-to-pdf-settings").then((m) => ({
default: m.MarkdownToPdfSettings,
})),
);
const ExcelToPdfSettings = lazy(() =>
import("@/components/tools/excel-to-pdf-settings").then((m) => ({
default: m.ExcelToPdfSettings,
})),
);
const PowerpointToPdfSettings = lazy(() =>
import("@/components/tools/powerpoint-to-pdf-settings").then((m) => ({
default: m.PowerpointToPdfSettings,
})),
);
const ConvertDocumentSettings = lazy(() =>
import("@/components/tools/convert-document-settings").then((m) => ({
default: m.ConvertDocumentSettings,
})),
);
const ConvertSpreadsheetSettings = lazy(() =>
import("@/components/tools/convert-spreadsheet-settings").then((m) => ({
default: m.ConvertSpreadsheetSettings,
})),
);
const ConvertPresentationSettings = lazy(() =>
import("@/components/tools/convert-presentation-settings").then((m) => ({
default: m.ConvertPresentationSettings,
})),
);
const MarkdownToHtmlSettings = lazy(() =>
import("@/components/tools/markdown-to-html-settings").then((m) => ({
default: m.MarkdownToHtmlSettings,
})),
);
const MarkdownToDocxSettings = lazy(() =>
import("@/components/tools/markdown-to-docx-settings").then((m) => ({
default: m.MarkdownToDocxSettings,
})),
);
const EpubConvertSettings = lazy(() =>
import("@/components/tools/epub-convert-settings").then((m) => ({
default: m.EpubConvertSettings,
})),
);
const ToEpubSettings = lazy(() =>
import("@/components/tools/to-epub-settings").then((m) => ({
default: m.ToEpubSettings,
})),
);
const MergeCsvsSettings = lazy(() =>
import("@/components/tools/merge-csvs-settings").then((m) => ({
default: m.MergeCsvsSettings,
})),
);
const YamlJsonSettings = lazy(() =>
import("@/components/tools/yaml-json-settings").then((m) => ({
default: m.YamlJsonSettings,
})),
);
const XmlToCsvSettings = lazy(() =>
import("@/components/tools/xml-to-csv-settings").then((m) => ({
default: m.XmlToCsvSettings,
})),
);
const CreateZipSettings = lazy(() =>
import("@/components/tools/create-zip-settings").then((m) => ({
default: m.CreateZipSettings,
})),
);
const ExtractZipSettings = lazy(() =>
import("@/components/tools/extract-zip-settings").then((m) => ({
default: m.ExtractZipSettings,
})),
);
// Wave 5a: Image gap-fill tools
const BarcodeGenerateSettings = lazy(() =>
import("@/components/tools/barcode-generate-settings").then((m) => ({
default: m.BarcodeGenerateSettings,
})),
);
const ChartMakerSettings = lazy(() =>
import("@/components/tools/chart-maker-settings").then((m) => ({
default: m.ChartMakerSettings,
})),
);
const CircleCropSettings = lazy(() =>
import("@/components/tools/circle-crop-settings").then((m) => ({
default: m.CircleCropSettings,
})),
);
const DuotoneSettings = lazy(() =>
import("@/components/tools/duotone-settings").then((m) => ({
default: m.DuotoneSettings,
})),
);
const GifWebpSettings = lazy(() =>
import("@/components/tools/gif-webp-settings").then((m) => ({
default: m.GifWebpSettings,
})),
);
const HistogramSettings = lazy(() =>
import("@/components/tools/histogram-settings").then((m) => ({
default: m.HistogramSettings,
})),
);
const ImagePadSettings = lazy(() =>
import("@/components/tools/image-pad-settings").then((m) => ({
default: m.ImagePadSettings,
})),
);
const LqipPlaceholderSettings = lazy(() =>
import("@/components/tools/lqip-placeholder-settings").then((m) => ({
default: m.LqipPlaceholderSettings,
})),
);
const PixelateSettings = lazy(() =>
import("@/components/tools/pixelate-settings").then((m) => ({
default: m.PixelateSettings,
})),
);
const SpriteSheetSettings = lazy(() =>
import("@/components/tools/sprite-sheet-settings").then((m) => ({
default: m.SpriteSheetSettings,
})),
);
const VignetteSettings = lazy(() =>
import("@/components/tools/vignette-settings").then((m) => ({
default: m.VignetteSettings,
})),
);
// ── Color tool wrapper ─────────────────────────────────────────────
// Color tools share a single component but differ by toolId.
function makeColorSettingsComponent(
toolId: string,
): React.ComponentType<{ onPreviewFilter?: (filter: string) => void }> {
return function ColorSettingsForTool(props: { onPreviewFilter?: (filter: string) => void }) {
return <ColorSettings toolId={toolId} onPreviewFilter={props.onPreviewFilter} />;
};
}
// ── Crop/Eraser wrappers ───────────────────────────────────────────
// These tools need special props that are passed through the registry.
function CropSettingsWrapper(props: { cropProps?: CropProps }) {
if (!props.cropProps) return null;
return <CropSettings {...props.cropProps} />;
}
function EraseObjectSettingsWrapper(props: { eraserProps?: EraserProps }) {
if (!props.eraserProps) return null;
return <EraseObjectSettings {...props.eraserProps} />;
}
// ── The registry ───────────────────────────────────────────────────
// Display modes come from TOOL_DISPLAY_MODES (tool-display-modes.ts); entries
// here hold only the React-side pieces (Settings, ResultsPanel, livePreview,
// accept). The merge below throws at module load if either side is missing a
// tool, so drift between the two files cannot ship.
type RegistryEntryConfig = Omit<ToolRegistryEntry, "displayMode">;
const ENTRY_CONFIG: ReadonlyArray<[string, RegistryEntryConfig]> = [
// Essentials
["resize", { Settings: ResizeSettings }],
["crop", { Settings: CropSettingsWrapper as never }],
["rotate", { livePreview: true, Settings: RotateSettings as never }],
["convert", { Settings: ConvertSettings }],
["compress", { Settings: CompressSettings }],
["strip-metadata", { Settings: StripMetadataSettings }],
["edit-metadata", { Settings: EditMetadataSettings }],
// Color adjustments (consolidated)
[
"adjust-colors",
{ livePreview: true, Settings: makeColorSettingsComponent("adjust-colors") as never },
],
// Sharpening
["sharpening", { Settings: SharpeningSettings }],
// Watermark & Overlay
["watermark-text", { Settings: WatermarkTextSettings }],
["watermark-image", { Settings: WatermarkImageSettings }],
["text-overlay", { Settings: TextOverlaySettings }],
["compose", { Settings: ComposeSettings }],
["meme-generator", { Settings: MemeGeneratorSettings, ResultsPanel: MemeGeneratorPreview }],
// Utilities
["info", { Settings: InfoSettings }],
["compare", { Settings: CompareSettings }],
["find-duplicates", { Settings: FindDuplicatesSettings, ResultsPanel: FindDuplicatesResults }],
["color-palette", { Settings: ColorPaletteSettings }],
["qr-generate", { Settings: QrGenerateSettings, ResultsPanel: QrGeneratePreview }],
["html-to-image", { Settings: HtmlToImageSettings, ResultsPanel: HtmlToImageResults }],
["barcode-read", { Settings: BarcodeReadSettings }],
["barcode-generate", { Settings: BarcodeGenerateSettings }],
["image-to-base64", { Settings: ImageToBase64Settings, ResultsPanel: ImageToBase64Results }],
// Layout & Composition
["collage", { Settings: CollageSettings, ResultsPanel: CollagePreview }],
["stitch", { Settings: StitchSettings }],
["split", { Settings: SplitSettings, ResultsPanel: SplitCanvas }],
["border", { livePreview: true, Settings: BorderSettings as never }],
["beautify", { livePreview: true, Settings: BeautifySettings as never }],
["circle-crop", { livePreview: true, Settings: CircleCropSettings }],
["duotone", { livePreview: true, Settings: DuotoneSettings }],
["image-pad", { livePreview: true, Settings: ImagePadSettings }],
["pixelate", { livePreview: true, Settings: PixelateSettings }],
["sprite-sheet", { Settings: SpriteSheetSettings }],
["vignette", { livePreview: true, Settings: VignetteSettings }],
// Image utilities (wave 5a)
["gif-webp", { livePreview: true, accept: ".gif,.webp", Settings: GifWebpSettings }],
["histogram", { Settings: HistogramSettings }],
["lqip-placeholder", { Settings: LqipPlaceholderSettings }],
// Format & Conversion
["svg-to-raster", { accept: ".svg,.svgz", Settings: SvgToRasterSettings }],
["vectorize", { Settings: VectorizeSettings }],
["gif-tools", { Settings: GifToolsSettings }],
// Optimization extras
["bulk-rename", { Settings: BulkRenameSettings }],
["favicon", { Settings: FaviconSettings }],
["image-to-pdf", { Settings: ImageToPdfSettings }],
["optimize-for-web", { Settings: OptimizeForWebSettings }],
[
"pdf-to-image",
{ accept: ".pdf", Settings: PdfToImageSettings, ResultsPanel: PdfToImagePreview },
],
// Adjustments extra
["replace-color", { Settings: ReplaceColorSettings }],
["color-blindness", { Settings: ColorBlindnessSettings }],
// AI Tools
["remove-background", { Settings: RemoveBgSettings }],
["upscale", { Settings: UpscaleSettings }],
["ocr", { Settings: OcrSettings }],
["ocr-pdf", { accept: ".pdf", Settings: OcrPdfSettings }],
["blur-faces", { Settings: BlurFacesSettings }],
["enhance-faces", { Settings: EnhanceFacesSettings }],
["erase-object", { Settings: EraseObjectSettingsWrapper as never }],
["smart-crop", { Settings: SmartCropSettings }],
["image-enhancement", { livePreview: true, Settings: ImageEnhancementSettings as never }],
["colorize", { Settings: ColorizeSettings }],
["noise-removal", { Settings: NoiseRemovalSettings }],
["passport-photo", { Settings: PassportPhotoSettings, ResultsPanel: PassportPhotoPreview }],
["red-eye-removal", { Settings: RedEyeRemovalSettings }],
["restore-photo", { Settings: RestorePhotoSettings }],
["transparency-fixer", { Settings: TransparencyFixerSettings }],
["content-aware-resize", { Settings: ContentAwareResizeSettings }],
["ai-canvas-expand", { Settings: AiCanvasExpandSettings }],
["transcribe-audio", { accept: AUDIO_INPUTS.join(","), Settings: TranscribeAudioSettings }],
["auto-subtitles", { accept: VIDEO_INPUTS.join(","), Settings: AutoSubtitlesSettings }],
["background-replace", { Settings: BackgroundReplaceSettings }],
["blur-background", { Settings: BlurBackgroundSettings }],
// Video tools
["convert-video", { accept: VIDEO_INPUTS.join(","), Settings: ConvertVideoSettings }],
["compress-video", { accept: VIDEO_INPUTS.join(","), Settings: CompressVideoSettings }],
["trim-video", { accept: VIDEO_INPUTS.join(","), Settings: TrimVideoSettings }],
["mute-video", { accept: VIDEO_INPUTS.join(","), Settings: MuteVideoSettings }],
["video-to-gif", { accept: VIDEO_INPUTS.join(","), Settings: VideoToGifSettings }],
["resize-video", { accept: VIDEO_INPUTS.join(","), Settings: ResizeVideoSettings }],
["crop-video", { accept: VIDEO_INPUTS.join(","), Settings: CropVideoSettings }],
["rotate-video", { accept: VIDEO_INPUTS.join(","), Settings: RotateVideoSettings }],
["change-fps", { accept: VIDEO_INPUTS.join(","), Settings: ChangeFpsSettings }],
["video-color", { accept: VIDEO_INPUTS.join(","), Settings: VideoColorSettings }],
["video-speed", { accept: VIDEO_INPUTS.join(","), Settings: VideoSpeedSettings }],
["reverse-video", { accept: VIDEO_INPUTS.join(","), Settings: ReverseVideoSettings }],
["video-loudnorm", { accept: VIDEO_INPUTS.join(","), Settings: VideoLoudnormSettings }],
["aspect-pad", { accept: VIDEO_INPUTS.join(","), Settings: AspectPadSettings }],
["blur-pad", { accept: VIDEO_INPUTS.join(","), Settings: BlurPadSettings }],
["watermark-video", { accept: VIDEO_INPUTS.join(","), Settings: WatermarkVideoSettings }],
["stabilize-video", { accept: VIDEO_INPUTS.join(","), Settings: StabilizeVideoSettings }],
["gif-to-video", { accept: ".gif", Settings: GifToVideoSettings }],
["video-to-webp", { accept: VIDEO_INPUTS.join(","), Settings: VideoToWebpSettings }],
["video-to-frames", { accept: VIDEO_INPUTS.join(","), Settings: VideoToFramesSettings }],
["merge-videos", { accept: VIDEO_INPUTS.join(","), Settings: MergeVideosSettings }],
[
"replace-audio",
{
accept: `${VIDEO_INPUTS.join(",")},${AUDIO_INPUTS.join(",")}`,
Settings: ReplaceAudioSettings,
},
],
[
"burn-subtitles",
{
accept: `${VIDEO_INPUTS.join(",")},${SUBTITLE_INPUTS.join(",")}`,
Settings: BurnSubtitlesSettings,
},
],
[
"embed-subtitles",
{
accept: `${VIDEO_INPUTS.join(",")},${SUBTITLE_INPUTS.join(",")}`,
Settings: EmbedSubtitlesSettings,
},
],
["extract-subtitles", { accept: VIDEO_INPUTS.join(","), Settings: ExtractSubtitlesSettings }],
["images-to-video", { accept: IMAGE_INPUTS.join(","), Settings: ImagesToVideoSettings }],
["video-metadata", { accept: VIDEO_INPUTS.join(","), Settings: VideoMetadataSettings }],
// Audio tools
["convert-audio", { accept: AUDIO_INPUTS.join(","), Settings: ConvertAudioSettings }],
["trim-audio", { accept: AUDIO_INPUTS.join(","), Settings: TrimAudioSettings }],
["extract-audio", { accept: VIDEO_INPUTS.join(","), Settings: ExtractAudioSettings }],
// Audio depth tools
["audio-channels", { accept: AUDIO_INPUTS.join(","), Settings: AudioChannelsSettings }],
["audio-metadata", { accept: AUDIO_INPUTS.join(","), Settings: AudioMetadataSettings }],
["audio-speed", { accept: AUDIO_INPUTS.join(","), Settings: AudioSpeedSettings }],
["fade-audio", { accept: AUDIO_INPUTS.join(","), Settings: FadeAudioSettings }],
["merge-audio", { accept: AUDIO_INPUTS.join(","), Settings: MergeAudioSettings }],
["noise-reduction", { accept: AUDIO_INPUTS.join(","), Settings: NoiseReductionSettings }],
["normalize-audio", { accept: AUDIO_INPUTS.join(","), Settings: NormalizeAudioSettings }],
["pitch-shift", { accept: AUDIO_INPUTS.join(","), Settings: PitchShiftSettings }],
["reverse-audio", { accept: AUDIO_INPUTS.join(","), Settings: ReverseAudioSettings }],
["ringtone-maker", { accept: AUDIO_INPUTS.join(","), Settings: RingtoneMakerSettings }],
["silence-removal", { accept: AUDIO_INPUTS.join(","), Settings: SilenceRemovalSettings }],
["split-audio", { accept: AUDIO_INPUTS.join(","), Settings: SplitAudioSettings }],
["volume-adjust", { accept: AUDIO_INPUTS.join(","), Settings: VolumeAdjustSettings }],
["waveform-image", { accept: AUDIO_INPUTS.join(","), Settings: WaveformImageSettings }],
// PDF & Document tools
["merge-pdf", { accept: ".pdf", Settings: MergePdfSettings }],
["split-pdf", { accept: ".pdf", Settings: SplitPdfSettings }],
["compress-pdf", { accept: ".pdf", Settings: CompressPdfSettings }],
["rotate-pdf", { accept: ".pdf", Settings: RotatePdfSettings }],
["word-to-pdf", { accept: ".docx,.doc,.odt,.rtf,.txt", Settings: WordToPdfSettings }],
["excel-to-pdf", { accept: ".xlsx,.xls,.ods,.csv", Settings: ExcelToPdfSettings }],
["powerpoint-to-pdf", { accept: ".pptx,.ppt,.odp", Settings: PowerpointToPdfSettings }],
["convert-document", { accept: ".docx,.doc,.odt,.rtf,.txt", Settings: ConvertDocumentSettings }],
["convert-spreadsheet", { accept: ".xlsx,.xls,.ods,.csv", Settings: ConvertSpreadsheetSettings }],
["convert-presentation", { accept: ".pptx,.ppt,.odp", Settings: ConvertPresentationSettings }],
["markdown-to-html", { accept: ".md,.markdown", Settings: MarkdownToHtmlSettings }],
["markdown-to-docx", { accept: ".md,.markdown", Settings: MarkdownToDocxSettings }],
["epub-convert", { accept: ".epub", Settings: EpubConvertSettings }],
["to-epub", { accept: ".docx,.md,.html,.txt", Settings: ToEpubSettings }],
// PDF depth tools (organize, secure, pdfcpu, layout)
["extract-pages", { accept: ".pdf", Settings: ExtractPagesSettings }],
["remove-pages", { accept: ".pdf", Settings: RemovePagesSettings }],
["organize-pdf", { accept: ".pdf", Settings: OrganizePdfSettings }],
["protect-pdf", { accept: ".pdf", Settings: ProtectPdfSettings }],
["unlock-pdf", { accept: ".pdf", Settings: UnlockPdfSettings }],
["repair-pdf", { accept: ".pdf", Settings: RepairPdfSettings }],
["crop-pdf", { accept: ".pdf", Settings: CropPdfSettings }],
["nup-pdf", { accept: ".pdf", Settings: NupPdfSettings }],
["booklet-pdf", { accept: ".pdf", Settings: BookletPdfSettings }],
["watermark-pdf", { accept: ".pdf", Settings: WatermarkPdfSettings }],
["pdf-page-numbers", { accept: ".pdf", Settings: PdfPageNumbersSettings }],
// PDF convert and optimize tools
["linearize-pdf", { accept: ".pdf", Settings: LinearizePdfSettings }],
["grayscale-pdf", { accept: ".pdf", Settings: GrayscalePdfSettings }],
["pdfa-convert", { accept: ".pdf", Settings: PdfaConvertSettings }],
["flatten-pdf", { accept: ".pdf", Settings: FlattenPdfSettings }],
["redact-pdf", { accept: ".pdf", Settings: RedactPdfSettings }],
["pdf-to-text", { accept: ".pdf", Settings: PdfToTextSettings }],
["pdf-to-word", { accept: ".pdf", Settings: PdfToWordSettings }],
["pdf-metadata", { accept: ".pdf", Settings: PdfMetadataSettings }],
["html-to-pdf", { accept: ".html,.htm", Settings: HtmlToPdfSettings }],
["markdown-to-pdf", { accept: ".md,.markdown", Settings: MarkdownToPdfSettings }],
// Data tools
["chart-maker", { accept: ".csv,.json", Settings: ChartMakerSettings }],
["csv-excel", { accept: ".csv,.tsv,.xlsx", Settings: CsvExcelSettings }],
["csv-json", { accept: ".csv,.tsv,.json", Settings: CsvJsonSettings }],
["json-xml", { accept: ".json,.xml", Settings: JsonXmlSettings }],
["split-csv", { accept: ".csv,.tsv", Settings: SplitCsvSettings }],
["merge-csvs", { accept: ".csv,.tsv", Settings: MergeCsvsSettings }],
["yaml-json", { accept: ".yaml,.yml,.json", Settings: YamlJsonSettings }],
["xml-to-csv", { accept: ".xml", Settings: XmlToCsvSettings }],
["create-zip", { Settings: CreateZipSettings }],
["extract-zip", { accept: ".zip", Settings: ExtractZipSettings }],
];
export const toolRegistry = new Map<string, ToolRegistryEntry>(
ENTRY_CONFIG.map(([toolId, entry]) => {
const displayMode = TOOL_DISPLAY_MODES[toolId];
if (!displayMode) {
throw new Error(`Tool "${toolId}" has no display mode in tool-display-modes.ts`);
}
return [toolId, { ...entry, displayMode }];
}),
);
export function getToolRegistryEntry(toolId: string): ToolRegistryEntry | undefined {
return toolRegistry.get(toolId);
}