[dyad] Added tooltips to all settings - wrote 1 file(s)

This commit is contained in:
[dyad]
2026-01-18 12:28:18 +01:00
parent db9f240222
commit 4cd5a4b171

View File

@@ -17,7 +17,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Upload, Download, X, Trash2, Check, ArrowRightLeft } from "lucide-react"; import { Upload, Download, X, Trash2, Check, ArrowRightLeft, HelpCircle } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
@@ -30,6 +30,12 @@ import {
} from "@/components/ui/accordion"; } from "@/components/ui/accordion";
import { Slider } from "@/components/ui/slider"; import { Slider } from "@/components/ui/slider";
import { ObjectPositionControl } from "./object-position-control"; import { ObjectPositionControl } from "./object-position-control";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
const aspectRatios = [ const aspectRatios = [
{ name: "Custom", value: "custom" }, { name: "Custom", value: "custom" },
@@ -359,6 +365,7 @@ export function ImageConverter() {
}; };
return ( return (
<TooltipProvider>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 w-full"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 w-full">
<div className="lg:col-span-1 flex flex-col gap-4 lg:sticky lg:top-8 self-start"> <div className="lg:col-span-1 flex flex-col gap-4 lg:sticky lg:top-8 self-start">
<Accordion type="single" collapsible defaultValue="image-settings" className="w-full space-y-4"> <Accordion type="single" collapsible defaultValue="image-settings" className="w-full space-y-4">
@@ -374,7 +381,14 @@ export function ImageConverter() {
<AccordionContent className="px-6 pb-6"> <AccordionContent className="px-6 pb-6">
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="aspect-ratio">Aspect Ratio</Label> <Label htmlFor="aspect-ratio">Aspect Ratio</Label>
</TooltipTrigger>
<TooltipContent>
<p>Choose a preset aspect ratio or select 'Custom' to enter dimensions manually.</p>
</TooltipContent>
</Tooltip>
<Select value={aspectRatio} onValueChange={handleAspectRatioChange}> <Select value={aspectRatio} onValueChange={handleAspectRatioChange}>
<SelectTrigger id="aspect-ratio" className="mt-2"> <SelectTrigger id="aspect-ratio" className="mt-2">
<SelectValue placeholder="Select aspect ratio" /> <SelectValue placeholder="Select aspect ratio" />
@@ -390,24 +404,59 @@ export function ImageConverter() {
</div> </div>
<div className="flex items-end gap-2"> <div className="flex items-end gap-2">
<div className="space-y-2 flex-1"> <div className="space-y-2 flex-1">
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="width">Width (px)</Label> <Label htmlFor="width">Width (px)</Label>
</TooltipTrigger>
<TooltipContent>
<p>Set the output width in pixels. Leave blank to use the original width.</p>
</TooltipContent>
</Tooltip>
<Input id="width" type="number" placeholder="Original" value={width} onChange={handleWidthChange} /> <Input id="width" type="number" placeholder="Original" value={width} onChange={handleWidthChange} />
</div> </div>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline" size="icon" onClick={handleSwapDimensions} className="shrink-0" aria-label="Swap width and height"> <Button variant="outline" size="icon" onClick={handleSwapDimensions} className="shrink-0" aria-label="Swap width and height">
<ArrowRightLeft className="h-4 w-4" /> <ArrowRightLeft className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>Swap the entered width and height values.</p>
</TooltipContent>
</Tooltip>
<div className="space-y-2 flex-1"> <div className="space-y-2 flex-1">
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="height">Height (px)</Label> <Label htmlFor="height">Height (px)</Label>
</TooltipTrigger>
<TooltipContent>
<p>Set the output height in pixels. Leave blank to use the original height.</p>
</TooltipContent>
</Tooltip>
<Input id="height" type="number" placeholder="Original" value={height} onChange={handleHeightChange} /> <Input id="height" type="number" placeholder="Original" value={height} onChange={handleHeightChange} />
</div> </div>
</div> </div>
<div className="flex items-center space-x-2 pt-2"> <div className="flex items-center space-x-2 pt-2">
<Checkbox id="keep-orientation" checked={keepOrientation} onCheckedChange={(checked) => setKeepOrientation(Boolean(checked))} /> <Checkbox id="keep-orientation" checked={keepOrientation} onCheckedChange={(checked) => setKeepOrientation(Boolean(checked))} />
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="keep-orientation" className="cursor-pointer">Keep original orientation</Label> <Label htmlFor="keep-orientation" className="cursor-pointer">Keep original orientation</Label>
</TooltipTrigger>
<TooltipContent>
<p>Automatically swaps width and height to match the original image's orientation.</p>
</TooltipContent>
</Tooltip>
</div> </div>
</div> </div>
<div className="mt-4 space-y-2"> <div className="mt-4 space-y-2">
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="scale-mode">Scaling</Label> <Label htmlFor="scale-mode">Scaling</Label>
</TooltipTrigger>
<TooltipContent>
<p>Determines how the image fits into the new dimensions.</p>
</TooltipContent>
</Tooltip>
<Select value={scaleMode} onValueChange={(value: 'fill' | 'cover' | 'contain') => setScaleMode(value)}> <Select value={scaleMode} onValueChange={(value: 'fill' | 'cover' | 'contain') => setScaleMode(value)}>
<SelectTrigger id="scale-mode"><SelectValue placeholder="Select scaling mode" /></SelectTrigger> <SelectTrigger id="scale-mode"><SelectValue placeholder="Select scaling mode" /></SelectTrigger>
<SelectContent> <SelectContent>
@@ -419,7 +468,14 @@ export function ImageConverter() {
</div> </div>
{scaleMode !== 'fill' && ( {scaleMode !== 'fill' && (
<div className="mt-4 space-y-2"> <div className="mt-4 space-y-2">
<Tooltip>
<TooltipTrigger asChild>
<Label>Position</Label> <Label>Position</Label>
</TooltipTrigger>
<TooltipContent>
<p>Sets the anchor point for 'Cover' or 'Contain' scaling.</p>
</TooltipContent>
</Tooltip>
<ObjectPositionControl value={objectPosition} onChange={(pos) => setObjectPosition(pos)} /> <ObjectPositionControl value={objectPosition} onChange={(pos) => setObjectPosition(pos)} />
</div> </div>
)} )}
@@ -437,7 +493,14 @@ export function ImageConverter() {
<div className="space-y-6"> <div className="space-y-6">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Switch id="use-default-base-name" checked={useDefaultBaseName} onCheckedChange={setUseDefaultBaseName} /> <Switch id="use-default-base-name" checked={useDefaultBaseName} onCheckedChange={setUseDefaultBaseName} />
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="use-default-base-name">Use default base name</Label> <Label htmlFor="use-default-base-name">Use default base name</Label>
</TooltipTrigger>
<TooltipContent>
<p>When enabled, all newly uploaded images will use the specified default base name.</p>
</TooltipContent>
</Tooltip>
</div> </div>
{useDefaultBaseName && ( {useDefaultBaseName && (
<div className="space-y-2"> <div className="space-y-2">
@@ -449,28 +512,63 @@ export function ImageConverter() {
value={defaultBaseName} value={defaultBaseName}
onChange={(e) => setDefaultBaseName(e.target.value)} onChange={(e) => setDefaultBaseName(e.target.value)}
/> />
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline" size="sm" onClick={handleApplyDefaultBaseNameToAll} disabled={!defaultBaseName || !hasImages}> <Button variant="outline" size="sm" onClick={handleApplyDefaultBaseNameToAll} disabled={!defaultBaseName || !hasImages}>
Apply to all Apply to all
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>Apply this base name to all currently uploaded images.</p>
</TooltipContent>
</Tooltip>
</div> </div>
</div> </div>
)} )}
<div className="space-y-2"> <div className="space-y-2">
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="prefix">Prefix</Label> <Label htmlFor="prefix">Prefix</Label>
</TooltipTrigger>
<TooltipContent>
<p>Add text to the beginning of every filename.</p>
</TooltipContent>
</Tooltip>
<Input id="prefix" placeholder="e.g., travel-" value={prefix} onChange={(e) => setPrefix(e.target.value)} /> <Input id="prefix" placeholder="e.g., travel-" value={prefix} onChange={(e) => setPrefix(e.target.value)} />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="suffix">Suffix</Label> <Label htmlFor="suffix">Suffix</Label>
</TooltipTrigger>
<TooltipContent>
<p>Add text to the end of every filename (before the number).</p>
</TooltipContent>
</Tooltip>
<Input id="suffix" placeholder="e.g., -edit" value={suffix} onChange={(e) => setSuffix(e.target.value)} /> <Input id="suffix" placeholder="e.g., -edit" value={suffix} onChange={(e) => setSuffix(e.target.value)} />
</div> </div>
<div className="flex items-center space-x-2 pt-2"> <div className="flex items-center space-x-2 pt-2">
<Switch id="use-counter" checked={useCounter} onCheckedChange={setUseCounter} /> <Switch id="use-counter" checked={useCounter} onCheckedChange={setUseCounter} />
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="use-counter">Add sequential number</Label> <Label htmlFor="use-counter">Add sequential number</Label>
</TooltipTrigger>
<TooltipContent>
<p>Append a numbered sequence to each filename.</p>
</TooltipContent>
</Tooltip>
</div> </div>
{useCounter && ( {useCounter && (
<div className="grid grid-cols-2 gap-4 pt-2"> <div className="grid grid-cols-2 gap-4 pt-2">
<div className="space-y-2"> <div className="space-y-2">
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="counter-start">Start number</Label> <Label htmlFor="counter-start">Start number</Label>
</TooltipTrigger>
<TooltipContent>
<p>The first number to use in the sequence.</p>
</TooltipContent>
</Tooltip>
<Input <Input
id="counter-start" id="counter-start"
type="number" type="number"
@@ -480,7 +578,14 @@ export function ImageConverter() {
/> />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="counter-digits">Padding digits</Label> <Label htmlFor="counter-digits">Padding digits</Label>
</TooltipTrigger>
<TooltipContent>
<p>Total number of digits for the counter, padded with leading zeros (e.g., 3 for 001).</p>
</TooltipContent>
</Tooltip>
<Input <Input
id="counter-digits" id="counter-digits"
type="number" type="number"
@@ -505,7 +610,14 @@ export function ImageConverter() {
<AccordionContent className="px-6 pb-6"> <AccordionContent className="px-6 pb-6">
<div className="space-y-6"> <div className="space-y-6">
<div className="space-y-2"> <div className="space-y-2">
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="format">Format</Label> <Label htmlFor="format">Format</Label>
</TooltipTrigger>
<TooltipContent>
<p>Choose the output file format for the images.</p>
</TooltipContent>
</Tooltip>
<Select value={format} onValueChange={(value: "png" | "jpeg" | "webp") => setFormat(value)}> <Select value={format} onValueChange={(value: "png" | "jpeg" | "webp") => setFormat(value)}>
<SelectTrigger id="format"><SelectValue placeholder="Select format" /></SelectTrigger> <SelectTrigger id="format"><SelectValue placeholder="Select format" /></SelectTrigger>
<SelectContent> <SelectContent>
@@ -517,7 +629,14 @@ export function ImageConverter() {
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<Tooltip>
<TooltipTrigger asChild>
<Label htmlFor="quality">Quality</Label> <Label htmlFor="quality">Quality</Label>
</TooltipTrigger>
<TooltipContent>
<p>Set compression quality for JPEG/WEBP. Higher is better quality but larger file size.</p>
</TooltipContent>
</Tooltip>
<span className="text-sm text-muted-foreground">{quality}%</span> <span className="text-sm text-muted-foreground">{quality}%</span>
</div> </div>
<Slider <Slider
@@ -537,6 +656,8 @@ export function ImageConverter() {
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
</Accordion> </Accordion>
<Tooltip>
<TooltipTrigger asChild>
<Button <Button
onClick={handleApplySettings} onClick={handleApplySettings}
disabled={!hasImages} disabled={!hasImages}
@@ -545,6 +666,11 @@ export function ImageConverter() {
<Check className="mr-2 h-4 w-4" /> <Check className="mr-2 h-4 w-4" />
Apply Settings Apply Settings
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>Confirm and apply all the settings above. This does not download the images.</p>
</TooltipContent>
</Tooltip>
</div> </div>
<div className="lg:col-span-2 flex flex-col gap-4"> <div className="lg:col-span-2 flex flex-col gap-4">
@@ -579,11 +705,25 @@ 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">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="sm" onClick={handleClearAll} disabled={isConverting || convertingIndex !== null}><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>
</TooltipTrigger>
<TooltipContent>
<p>Remove all uploaded images.</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button onClick={handleConvertAndDownloadAll} disabled={!hasImages || isConverting || convertingIndex !== null}> <Button onClick={handleConvertAndDownloadAll} disabled={!hasImages || 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>
</TooltipTrigger>
<TooltipContent>
<p>Convert and download all images with the current settings.</p>
</TooltipContent>
</Tooltip>
</div> </div>
</div> </div>
</CardHeader> </CardHeader>
@@ -609,26 +749,38 @@ export function ImageConverter() {
</p> </p>
</div> </div>
<div className="flex items-center shrink-0"> <div className="flex items-center shrink-0">
<Tooltip>
<TooltipTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="text-gray-500 hover:text-primary" className="text-gray-500 hover:text-primary"
onClick={() => handleConvertAndDownloadSingle(index)} onClick={() => handleConvertAndDownloadSingle(index)}
disabled={isConverting || convertingIndex !== null} disabled={isConverting || convertingIndex !== null}
title="Download this image"
> >
<Download className="h-4 w-4" /> <Download className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>Download this image</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="shrink-0 text-gray-500 hover:text-destructive" className="shrink-0 text-gray-500 hover:text-destructive"
onClick={() => handleRemoveImage(index)} onClick={() => handleRemoveImage(index)}
disabled={isConverting || convertingIndex !== null} disabled={isConverting || convertingIndex !== null}
title="Remove this image"
> >
<X className="h-4 w-4" /> <X className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>Remove this image</p>
</TooltipContent>
</Tooltip>
</div> </div>
</div> </div>
); );
@@ -639,5 +791,6 @@ export function ImageConverter() {
)} )}
</div> </div>
</div> </div>
</TooltipProvider>
); );
} }