import { CATEGORIES, PYTHON_SIDECAR_TOOLS, TOOL_BUNDLE_MAP, TOOLS } from "@snapotter/shared"; import { Clock, Download, Loader2 } from "lucide-react"; import { useCallback, useEffect, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { ImageViewer } from "@/components/common/image-viewer"; import { MultiImageViewer } from "@/components/common/multi-image-viewer"; import { AppLayout } from "@/components/layout/app-layout"; import { ICON_MAP } from "@/lib/icon-map"; import { useFeaturesStore } from "@/stores/features-store"; import { useFileStore } from "@/stores/file-store"; import { useSettingsStore } from "@/stores/settings-store"; // Tools shown prominently as "quick actions" at the top const QUICK_ACTION_IDS = ["resize", "compress", "convert", "remove-background"]; let hasAppliedDefaultRedirect = false; export function HomePage() { const { setFiles, files, reset, originalBlobUrl, selectedFileName, selectedFileSize, currentEntry, } = useFileStore(); const navigate = useNavigate(); const { fetch: fetchSettings, defaultToolView, loaded: settingsLoaded } = useSettingsStore(); const { fetch: fetchFeatures, bundles, installing, queued } = useFeaturesStore(); useEffect(() => { reset(); }, [reset]); useEffect(() => { fetchSettings(); fetchFeatures(); }, [fetchSettings, fetchFeatures]); useEffect(() => { if ( !hasAppliedDefaultRedirect && settingsLoaded && defaultToolView === "fullscreen" && files.length === 0 ) { hasAppliedDefaultRedirect = true; navigate("/fullscreen", { replace: true }); } }, [settingsLoaded, defaultToolView, files.length, navigate]); const getToolStatus = useMemo(() => { return (toolId: string) => { const isAi = (PYTHON_SIDECAR_TOOLS as readonly string[]).includes(toolId); if (!isAi) return "installed"; const bundleId = TOOL_BUNDLE_MAP[toolId]; if (!bundleId) return "installed"; if (queued.includes(bundleId)) return "queued"; if (installing[bundleId]) return "installing"; const bundle = bundles.find((b) => b.id === bundleId); return bundle?.status === "installed" ? "installed" : "not_installed"; }; }, [bundles, installing, queued]); const handleFiles = useCallback( (newFiles: File[]) => { reset(); setFiles(newFiles); }, [setFiles, reset], ); const hasFile = files.length > 0; // If no file uploaded, show default layout (tool panel + dropzone) if (!hasFile) { return ; } // File uploaded — show tool selector on left, image preview on right return ( {/* Left panel: Tool selector */} {/* File info */} {selectedFileName ?? files[0].name} {selectedFileSize ? `${(selectedFileSize / 1024).toFixed(1)} KB` : ""} {files.length > 1 && ` — ${files.length} files`} Change file {/* Quick actions */} Quick Actions {QUICK_ACTION_IDS.map((id) => { const tool = TOOLS.find((t) => t.id === id); if (!tool) return null; const Icon = (ICON_MAP[tool.icon] as React.ComponentType<{ className?: string }>) ?? ICON_MAP.FileImage; const status = getToolStatus(id); return ( navigate(tool.route)} className="flex items-center gap-2 p-3 rounded-xl border border-border hover:border-primary hover:bg-primary/5 transition-colors text-left" > {tool.name} {status === "not_installed" && ( )} {status === "queued" && ( )} {status === "installing" && ( )} ); })} {/* All tools by category */} All Tools {CATEGORIES.map((category) => { const categoryTools = TOOLS.filter((t) => t.category === category.id); if (categoryTools.length === 0) return null; return ( {category.name} {categoryTools.map((tool) => { const Icon = (ICON_MAP[tool.icon] as React.ComponentType<{ className?: string }>) ?? ICON_MAP.FileImage; const status = getToolStatus(tool.id); return ( navigate(tool.route)} className="flex items-center gap-2.5 w-full py-1.5 px-2 rounded-lg text-left transition-colors hover:bg-muted text-foreground" > {tool.name} {status === "not_installed" && ( )} {status === "queued" && ( )} {status === "installing" && ( )} ); })} ); })} {/* Right panel: Image preview */} {files.length > 1 ? ( ) : currentEntry?.previewLoading ? ( Generating preview... {selectedFileName} ) : originalBlobUrl ? ( ) : ( Loading preview... )} ); }
{selectedFileSize ? `${(selectedFileSize / 1024).toFixed(1)} KB` : ""} {files.length > 1 && ` — ${files.length} files`}
{category.name}
Generating preview...
{selectedFileName}
Loading preview...