[dyad] Improved download flexibility - wrote 1 file(s)

This commit is contained in:
[dyad]
2026-01-18 11:34:41 +01:00
parent c74f5dcc28
commit 18dd995fc4

View File

@@ -135,7 +135,7 @@ export function ImageConverter() {
setFilenames(newFilenames);
};
const generateFinalFilename = (index: number) => {
const generateFinalFilename = (index: number, withDimensions: boolean = false) => {
const baseName = filenames[index] || "filename";
let finalName = `${prefix}${baseName}${suffix}`;
@@ -144,39 +144,38 @@ export function ImageConverter() {
finalName += `${counter}`;
}
if (withDimensions && width && height) {
finalName += `_${width}x${height}`;
}
return finalName;
};
const handleConvertAndDownload = async () => {
if (images.length === 0 || !width || !height) {
toast.error("Please upload images and set dimensions.");
return;
}
setIsConverting(true);
toast.info(`Starting conversion for ${images.length} images...`);
const conversionPromises = images.map((image, index) => {
const convertAndDownload = (image: File, previewUrl: string, index: number) => {
return new Promise<void>((resolve, reject) => {
const previewUrl = previewUrls[index];
const img = new Image();
img.crossOrigin = "anonymous";
img.src = previewUrl;
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = Number(width);
canvas.height = Number(height);
const targetWidth = width ? Number(width) : img.naturalWidth;
const targetHeight = height ? Number(height) : img.naturalHeight;
canvas.width = targetWidth;
canvas.height = targetHeight;
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.drawImage(img, 0, 0, Number(width), Number(height));
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
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}`;
const dimensionSuffix = width && height ? `_${width}x${height}` : '';
link.download = `${generateFinalFilename(index)}${dimensionSuffix}.${format}`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
@@ -189,7 +188,20 @@ export function ImageConverter() {
reject(new Error(`Failed to load ${image.name} for conversion.`));
};
});
});
};
const handleConvertAndDownloadAll = async () => {
if (images.length === 0) {
toast.error("Please upload images first.");
return;
}
setIsConverting(true);
toast.info(`Starting conversion for ${images.length} images...`);
const conversionPromises = images.map((image, index) =>
convertAndDownload(image, previewUrls[index], index)
);
try {
await Promise.all(conversionPromises);
@@ -206,50 +218,11 @@ 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.`));
};
});
await convertAndDownload(images[index], previewUrls[index], index);
toast.success(`Successfully exported ${filenames[index]}!`);
} catch (error) {
if (error instanceof Error) {
@@ -281,11 +254,11 @@ export function ImageConverter() {
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="width">Width (px)</Label>
<Input id="width" type="number" placeholder="e.g., 1920" value={width} onChange={(e) => setWidth(e.target.value)} />
<Input id="width" type="number" placeholder="Original" value={width} onChange={(e) => setWidth(e.target.value)} />
</div>
<div className="space-y-2">
<Label htmlFor="height">Height (px)</Label>
<Input id="height" type="number" placeholder="e.g., 1080" value={height} onChange={(e) => setHeight(e.target.value)} />
<Input id="height" type="number" placeholder="Original" value={height} onChange={(e) => setHeight(e.target.value)} />
</div>
</div>
</AccordionContent>
@@ -382,14 +355,6 @@ export function ImageConverter() {
</AccordionContent>
</AccordionItem>
</Accordion>
<Button
onClick={handleConvertAndDownload}
disabled={!hasImages || !width || !height || isConverting || convertingIndex !== null}
className="w-full"
>
<Download className="mr-2 h-4 w-4" />
{isConverting ? "Converting..." : `Apply Settings & Download All (${images.length})`}
</Button>
</div>
<div className="lg:col-span-2 flex flex-col gap-8">
@@ -418,7 +383,7 @@ export function ImageConverter() {
<CardTitle>Uploaded Images</CardTitle>
<div className="flex items-center gap-2">
<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 || convertingIndex !== null}>
<Button onClick={handleConvertAndDownloadAll} disabled={!hasImages || isConverting || convertingIndex !== null}>
<Download className="mr-2 h-4 w-4" />
{isConverting ? "Converting..." : `Download All (${images.length})`}
</Button>
@@ -428,7 +393,9 @@ export function ImageConverter() {
<CardContent>
<ScrollArea className="h-[400px] pr-4">
<div className="space-y-4">
{previewUrls.map((url, index) => (
{previewUrls.map((url, index) => {
const finalFilename = generateFinalFilename(index, true);
return (
<div key={url} className="p-4 border rounded-lg flex items-center gap-4">
<img src={url} alt={`Preview ${index + 1}`} className="w-20 h-20 object-cover rounded-md shrink-0" />
<div className="flex-1 min-w-0">
@@ -439,8 +406,8 @@ export function ImageConverter() {
onChange={(e) => handleFilenameChange(index, e.target.value)}
className="text-sm font-medium h-8 mt-1"
/>
<p className="text-xs text-muted-foreground truncate mt-1" title={`${generateFinalFilename(index)}_${width || 'w'}x${height || 'h'}.${format}`}>
Final name: {generateFinalFilename(index)}.{format}
<p className="text-xs text-muted-foreground truncate mt-1" title={`${finalFilename}.${format}`}>
Final name: {finalFilename}.{format}
</p>
</div>
<div className="flex items-center shrink-0">
@@ -449,7 +416,7 @@ export function ImageConverter() {
size="icon"
className="text-gray-500 hover:text-primary"
onClick={() => handleConvertAndDownloadSingle(index)}
disabled={isConverting || convertingIndex !== null || !width || !height}
disabled={isConverting || convertingIndex !== null}
title="Download this image"
>
<Download className="h-4 w-4" />
@@ -466,7 +433,8 @@ export function ImageConverter() {
</Button>
</div>
</div>
))}
);
})}
</div>
</ScrollArea>
</CardContent>