[dyad] Added primary download button - wrote 1 file(s)

This commit is contained in:
[dyad]
2026-01-18 11:27:36 +01:00
parent 871a3ed7fb
commit d55f88c40b

View File

@@ -208,130 +208,140 @@ export function ImageConverter() {
return ( return (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 w-full"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-8 w-full">
<div className="lg:col-span-1 flex flex-col"> <div className="lg:col-span-1 flex flex-col gap-8">
<Accordion type="single" collapsible defaultValue="image-settings" className="w-full space-y-8"> <Accordion type="single" collapsible defaultValue="image-settings" className="w-full">
<Card> <div className="space-y-8">
<AccordionItem value="image-settings" className="border-none"> <Card>
<AccordionTrigger className="p-6 hover:no-underline"> <AccordionItem value="image-settings" className="border-none">
<div className="text-left"> <AccordionTrigger className="p-6 hover:no-underline">
<CardTitle>Image Settings</CardTitle> <div className="text-left">
<CardDescription className="mt-1"> <CardTitle>Image Settings</CardTitle>
Adjust resolution for all uploaded images. <CardDescription className="mt-1">
</CardDescription> Adjust resolution for all uploaded images.
</div> </CardDescription>
</AccordionTrigger>
<AccordionContent className="px-6 pb-6">
<div className="space-y-6">
<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)} />
</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)} />
</div>
</div> </div>
</div> </AccordionTrigger>
</AccordionContent> <AccordionContent className="px-6 pb-6">
</AccordionItem> <div className="space-y-6">
</Card> <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<Card>
<AccordionItem value="filename-settings" className="border-none">
<AccordionTrigger className="p-6 hover:no-underline">
<div className="text-left">
<CardTitle>Filename Settings</CardTitle>
<CardDescription className="mt-1">Customize the output filenames.</CardDescription>
</div>
</AccordionTrigger>
<AccordionContent className="px-6 pb-6">
<div className="space-y-6">
<div className="space-y-2">
<Label htmlFor="prefix">Prefix</Label>
<Input id="prefix" placeholder="e.g., travel-" value={prefix} onChange={(e) => setPrefix(e.target.value)} />
</div>
<div className="space-y-2">
<Label htmlFor="suffix">Suffix</Label>
<Input id="suffix" placeholder="e.g., -edit" value={suffix} onChange={(e) => setSuffix(e.target.value)} />
</div>
<div className="flex items-center space-x-2 pt-2">
<Switch id="use-counter" checked={useCounter} onCheckedChange={setUseCounter} />
<Label htmlFor="use-counter">Add sequential number</Label>
</div>
{useCounter && (
<div className="grid grid-cols-2 gap-4 pt-2">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="counter-start">Start number</Label> <Label htmlFor="width">Width (px)</Label>
<Input <Input id="width" type="number" placeholder="e.g., 1920" value={width} onChange={(e) => setWidth(e.target.value)} />
id="counter-start"
type="number"
value={counterStart}
onChange={(e) => setCounterStart(Math.max(0, Number(e.target.value)))}
min="0"
/>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="counter-digits">Padding digits</Label> <Label htmlFor="height">Height (px)</Label>
<Input <Input id="height" type="number" placeholder="e.g., 1080" value={height} onChange={(e) => setHeight(e.target.value)} />
id="counter-digits"
type="number"
value={counterDigits}
onChange={(e) => setCounterDigits(Math.max(1, Number(e.target.value)))}
min="1"
/>
</div> </div>
</div> </div>
)}
</div>
</AccordionContent>
</AccordionItem>
</Card>
<Card>
<AccordionItem value="quality-settings" className="border-none">
<AccordionTrigger className="p-6 hover:no-underline">
<div className="text-left">
<CardTitle>Quality Settings</CardTitle>
<CardDescription className="mt-1">Choose format and compression level.</CardDescription>
</div>
</AccordionTrigger>
<AccordionContent className="px-6 pb-6">
<div className="space-y-6">
<div className="space-y-2">
<Label htmlFor="format">Format</Label>
<Select value={format} onValueChange={(value: "png" | "jpeg" | "webp") => setFormat(value)}>
<SelectTrigger id="format"><SelectValue placeholder="Select format" /></SelectTrigger>
<SelectContent>
<SelectItem value="png">PNG</SelectItem>
<SelectItem value="jpeg">JPEG</SelectItem>
<SelectItem value="webp">WEBP</SelectItem>
</SelectContent>
</Select>
</div> </div>
<div className="space-y-2"> </AccordionContent>
<div className="flex justify-between items-center"> </AccordionItem>
<Label htmlFor="quality">Quality</Label> </Card>
<span className="text-sm text-muted-foreground">{quality}%</span>
<Card>
<AccordionItem value="filename-settings" className="border-none">
<AccordionTrigger className="p-6 hover:no-underline">
<div className="text-left">
<CardTitle>Filename Settings</CardTitle>
<CardDescription className="mt-1">Customize the output filenames.</CardDescription>
</div>
</AccordionTrigger>
<AccordionContent className="px-6 pb-6">
<div className="space-y-6">
<div className="space-y-2">
<Label htmlFor="prefix">Prefix</Label>
<Input id="prefix" placeholder="e.g., travel-" value={prefix} onChange={(e) => setPrefix(e.target.value)} />
</div> </div>
<Slider <div className="space-y-2">
id="quality" <Label htmlFor="suffix">Suffix</Label>
min={0} <Input id="suffix" placeholder="e.g., -edit" value={suffix} onChange={(e) => setSuffix(e.target.value)} />
max={100} </div>
step={1} <div className="flex items-center space-x-2 pt-2">
value={[quality]} <Switch id="use-counter" checked={useCounter} onCheckedChange={setUseCounter} />
onValueChange={(value) => setQuality(value[0])} <Label htmlFor="use-counter">Add sequential number</Label>
disabled={format === 'png'} </div>
/> {useCounter && (
{format === 'png' && ( <div className="grid grid-cols-2 gap-4 pt-2">
<p className="text-xs text-muted-foreground pt-1">Quality slider is disabled for PNG (lossless format).</p> <div className="space-y-2">
<Label htmlFor="counter-start">Start number</Label>
<Input
id="counter-start"
type="number"
value={counterStart}
onChange={(e) => setCounterStart(Math.max(0, Number(e.target.value)))}
min="0"
/>
</div>
<div className="space-y-2">
<Label htmlFor="counter-digits">Padding digits</Label>
<Input
id="counter-digits"
type="number"
value={counterDigits}
onChange={(e) => setCounterDigits(Math.max(1, Number(e.target.value)))}
min="1"
/>
</div>
</div>
)} )}
</div> </div>
</div> </AccordionContent>
</AccordionContent> </AccordionItem>
</AccordionItem> </Card>
</Card>
<Card>
<AccordionItem value="quality-settings" className="border-none">
<AccordionTrigger className="p-6 hover:no-underline">
<div className="text-left">
<CardTitle>Quality Settings</CardTitle>
<CardDescription className="mt-1">Choose format and compression level.</CardDescription>
</div>
</AccordionTrigger>
<AccordionContent className="px-6 pb-6">
<div className="space-y-6">
<div className="space-y-2">
<Label htmlFor="format">Format</Label>
<Select value={format} onValueChange={(value: "png" | "jpeg" | "webp") => setFormat(value)}>
<SelectTrigger id="format"><SelectValue placeholder="Select format" /></SelectTrigger>
<SelectContent>
<SelectItem value="png">PNG</SelectItem>
<SelectItem value="jpeg">JPEG</SelectItem>
<SelectItem value="webp">WEBP</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<div className="flex justify-between items-center">
<Label htmlFor="quality">Quality</Label>
<span className="text-sm text-muted-foreground">{quality}%</span>
</div>
<Slider
id="quality"
min={0}
max={100}
step={1}
value={[quality]}
onValueChange={(value) => setQuality(value[0])}
disabled={format === 'png'}
/>
{format === 'png' && (
<p className="text-xs text-muted-foreground pt-1">Quality slider is disabled for PNG (lossless format).</p>
)}
</div>
</div>
</AccordionContent>
</AccordionItem>
</Card>
</div>
</Accordion> </Accordion>
<Button
onClick={handleConvertAndDownload}
disabled={!hasImages || !width || !height || isConverting}
className="w-full"
>
<Download className="mr-2 h-4 w-4" />
{isConverting ? "Converting..." : `Apply Settings & Download All (${images.length})`}
</Button>
</div> </div>
<div className="lg:col-span-2 flex flex-col gap-8"> <div className="lg:col-span-2 flex flex-col gap-8">