/** * Inline image preview for the project editor. * * Loads binary file as base64 data URL via IPC, displays centered image * with checkerboard background for transparency, metadata, and lightbox on click. */ import { useCallback, useEffect, useRef, useState } from 'react'; import { ImageLightbox } from '@renderer/components/team/attachments/ImageLightbox'; import { Button } from '@renderer/components/ui/button'; import { useStore } from '@renderer/store'; import { Loader2 } from 'lucide-react'; import { EditorBinaryPlaceholder } from './EditorBinaryPlaceholder'; interface EditorImagePreviewProps { filePath: string; fileName: string; size: number; } export const EditorImagePreview = ({ filePath, fileName, size, }: EditorImagePreviewProps): React.ReactElement => { const projectPath = useStore((s) => s.editorProjectPath); const [dataUrl, setDataUrl] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [lightboxOpen, setLightboxOpen] = useState(false); const [dimensions, setDimensions] = useState<{ w: number; h: number } | null>(null); const imgRef = useRef(null); // Reset state when filePath changes useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional sync on prop change setLoading(true); setError(null); setDataUrl(null); setDimensions(null); setLightboxOpen(false); }, [filePath]); useEffect(() => { let cancelled = false; window.electronAPI.editor .readBinaryPreview(filePath) .then((result) => { if (cancelled) return; setDataUrl(`data:${result.mimeType};base64,${result.base64}`); }) .catch((err: Error) => { if (cancelled) return; setError(err.message); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [filePath]); const handleImageLoad = useCallback(() => { const img = imgRef.current; if (img) { setDimensions({ w: img.naturalWidth, h: img.naturalHeight }); } }, []); const handleOpenExternal = useCallback((): void => { window.electronAPI.openPath(filePath, projectPath ?? undefined).catch(console.error); }, [filePath, projectPath]); const sizeFormatted = size < 1024 ? `${size} B` : size < 1024 * 1024 ? `${(size / 1024).toFixed(1)} KB` : `${(size / 1024 / 1024).toFixed(1)} MB`; if (loading) { return (

Loading preview…

); } if (error || !dataUrl) { return ; } return (

{fileName} {dimensions ? ` — ${dimensions.w}×${dimensions.h}` : ''} {` — ${sizeFormatted}`}

setLightboxOpen(false)} src={dataUrl} alt={fileName} />
); };