[dyad] Added individual image download - wrote 1 file(s)

This commit is contained in:
[dyad]
2026-01-18 11:29:03 +01:00
parent d55f88c40b
commit 15deb9e044

View File

@@ -48,6 +48,7 @@ export function ImageConverter() {
const [counterDigits, setCounterDigits] = useState<number>(3); const [counterDigits, setCounterDigits] = useState<number>(3);
const [isConverting, setIsConverting] = useState(false); const [isConverting, setIsConverting] = useState(false);
const [convertingIndex, setConvertingIndex] = useState<number | null>(null);
const [isDraggingOver, setIsDraggingOver] = useState(false); const [isDraggingOver, setIsDraggingOver] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
@@ -204,6 +205,63 @@ export function ImageConverter() {
} }
}; };
const handleConvertAndDownloadSingle = async (index: number) => {
if (!width || !height) {
toast.error("Please set dimensions before downloading.");
return;
}
setConvertingIndex(index);
toast.info(`Starting conversion for ${filenames[index]}...`);
const image = images[index];
const previewUrl = previewUrls[index];
try {
const img = new Image();
img.crossOrigin = "anonymous";
img.src = previewUrl;
await new Promise<void>((resolve, reject) => {
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = Number(width);
canvas.height = Number(height);
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.drawImage(img, 0, 0, Number(width), Number(height));
const mimeType = `image/${format}`;
const dataUrl = canvas.toDataURL(mimeType, format === 'png' ? undefined : quality / 100);
const link = document.createElement("a");
link.href = dataUrl;
const finalFilename = generateFinalFilename(index);
link.download = `${finalFilename}_${width}x${height}.${format}`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
resolve();
} else {
reject(new Error(`Could not process ${image.name}.`));
}
};
img.onerror = () => {
reject(new Error(`Failed to load ${image.name} for conversion.`));
};
});
toast.success(`Successfully exported ${filenames[index]}!`);
} catch (error) {
if (error instanceof Error) {
toast.error(error.message);
} else {
toast.error("An unknown error occurred during conversion.");
}
} finally {
setConvertingIndex(null);
}
};
const hasImages = images.length > 0; const hasImages = images.length > 0;
return ( return (
@@ -336,7 +394,7 @@ export function ImageConverter() {
</Accordion> </Accordion>
<Button <Button
onClick={handleConvertAndDownload} onClick={handleConvertAndDownload}
disabled={!hasImages || !width || !height || isConverting} disabled={!hasImages || !width || !height || isConverting || convertingIndex !== null}
className="w-full" className="w-full"
> >
<Download className="mr-2 h-4 w-4" /> <Download className="mr-2 h-4 w-4" />
@@ -369,8 +427,8 @@ export function ImageConverter() {
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<CardTitle>Uploaded Images</CardTitle> <CardTitle>Uploaded Images</CardTitle>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button variant="ghost" size="sm" onClick={handleClearAll}><Trash2 className="mr-2 h-4 w-4" />Clear All</Button> <Button variant="ghost" size="sm" onClick={handleClearAll} disabled={isConverting || convertingIndex !== null}><Trash2 className="mr-2 h-4 w-4" />Clear All</Button>
<Button onClick={handleConvertAndDownload} disabled={!hasImages || !width || !height || isConverting}> <Button onClick={handleConvertAndDownload} disabled={!hasImages || !width || !height || isConverting || convertingIndex !== null}>
<Download className="mr-2 h-4 w-4" /> <Download className="mr-2 h-4 w-4" />
{isConverting ? "Converting..." : `Download All (${images.length})`} {isConverting ? "Converting..." : `Download All (${images.length})`}
</Button> </Button>
@@ -395,9 +453,28 @@ export function ImageConverter() {
Final name: {generateFinalFilename(index)}.{format} Final name: {generateFinalFilename(index)}.{format}
</p> </p>
</div> </div>
<Button variant="ghost" size="icon" className="shrink-0 text-gray-500 hover:text-destructive" onClick={() => handleRemoveImage(index)}> <div className="flex items-center shrink-0">
<X className="h-4 w-4" /> <Button
</Button> variant="ghost"
size="icon"
className="text-gray-500 hover:text-primary"
onClick={() => handleConvertAndDownloadSingle(index)}
disabled={isConverting || convertingIndex !== null || !width || !height}
title="Download this image"
>
<Download className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="shrink-0 text-gray-500 hover:text-destructive"
onClick={() => handleRemoveImage(index)}
disabled={isConverting || convertingIndex !== null}
title="Remove this image"
>
<X className="h-4 w-4" />
</Button>
</div>
</div> </div>
))} ))}
</div> </div>