[dyad] Enable per-image filename editing - wrote 1 file(s)
This commit is contained in:
@@ -34,7 +34,7 @@ import {
|
|||||||
export function ImageConverter() {
|
export function ImageConverter() {
|
||||||
const [images, setImages] = useState<File[]>([]);
|
const [images, setImages] = useState<File[]>([]);
|
||||||
const [previewUrls, setPreviewUrls] = useState<string[]>([]);
|
const [previewUrls, setPreviewUrls] = useState<string[]>([]);
|
||||||
const [originalFilenames, setOriginalFilenames] = useState<string[]>([]);
|
const [filenames, setFilenames] = useState<string[]>([]);
|
||||||
const [width, setWidth] = useState<number | string>("");
|
const [width, setWidth] = useState<number | string>("");
|
||||||
const [height, setHeight] = useState<number | string>("");
|
const [height, setHeight] = useState<number | string>("");
|
||||||
const [format, setFormat] = useState<"png" | "jpeg" | "webp">("png");
|
const [format, setFormat] = useState<"png" | "jpeg" | "webp">("png");
|
||||||
@@ -71,8 +71,8 @@ export function ImageConverter() {
|
|||||||
...previewUrls,
|
...previewUrls,
|
||||||
...imageFiles.map((file) => URL.createObjectURL(file)),
|
...imageFiles.map((file) => URL.createObjectURL(file)),
|
||||||
];
|
];
|
||||||
const newOriginalFilenames = [
|
const newFilenames = [
|
||||||
...originalFilenames,
|
...filenames,
|
||||||
...imageFiles.map((file) =>
|
...imageFiles.map((file) =>
|
||||||
file.name.substring(0, file.name.lastIndexOf("."))
|
file.name.substring(0, file.name.lastIndexOf("."))
|
||||||
),
|
),
|
||||||
@@ -80,7 +80,7 @@ export function ImageConverter() {
|
|||||||
|
|
||||||
setImages(newImages);
|
setImages(newImages);
|
||||||
setPreviewUrls(newPreviewUrls);
|
setPreviewUrls(newPreviewUrls);
|
||||||
setOriginalFilenames(newOriginalFilenames);
|
setFilenames(newFilenames);
|
||||||
|
|
||||||
toast.success(`${imageFiles.length} image(s) added.`);
|
toast.success(`${imageFiles.length} image(s) added.`);
|
||||||
};
|
};
|
||||||
@@ -110,28 +110,34 @@ export function ImageConverter() {
|
|||||||
URL.revokeObjectURL(previewUrls[indexToRemove]);
|
URL.revokeObjectURL(previewUrls[indexToRemove]);
|
||||||
const newImages = images.filter((_, i) => i !== indexToRemove);
|
const newImages = images.filter((_, i) => i !== indexToRemove);
|
||||||
const newPreviewUrls = previewUrls.filter((_, i) => i !== indexToRemove);
|
const newPreviewUrls = previewUrls.filter((_, i) => i !== indexToRemove);
|
||||||
const newOriginalFilenames = originalFilenames.filter((_, i) => i !== indexToRemove);
|
const newFilenames = filenames.filter((_, i) => i !== indexToRemove);
|
||||||
|
|
||||||
setImages(newImages);
|
setImages(newImages);
|
||||||
setPreviewUrls(newPreviewUrls);
|
setPreviewUrls(newPreviewUrls);
|
||||||
setOriginalFilenames(newOriginalFilenames);
|
setFilenames(newFilenames);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClearAll = () => {
|
const handleClearAll = () => {
|
||||||
previewUrls.forEach((url) => URL.revokeObjectURL(url));
|
previewUrls.forEach((url) => URL.revokeObjectURL(url));
|
||||||
setImages([]);
|
setImages([]);
|
||||||
setPreviewUrls([]);
|
setPreviewUrls([]);
|
||||||
setOriginalFilenames([]);
|
setFilenames([]);
|
||||||
toast.info("All images cleared.");
|
toast.info("All images cleared.");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleFilenameChange = (index: number, newName: string) => {
|
||||||
|
const newFilenames = [...filenames];
|
||||||
|
newFilenames[index] = newName;
|
||||||
|
setFilenames(newFilenames);
|
||||||
|
};
|
||||||
|
|
||||||
const generateFinalFilename = (index: number) => {
|
const generateFinalFilename = (index: number) => {
|
||||||
if (useCounter) {
|
if (useCounter) {
|
||||||
const counter = (index + 1).toString().padStart(counterDigits, '0');
|
const counter = (index + 1).toString().padStart(counterDigits, '0');
|
||||||
return `${prefix}${counter}${suffix}`;
|
return `${prefix}${counter}${suffix}`;
|
||||||
}
|
}
|
||||||
const originalName = originalFilenames[index] || "filename";
|
const baseName = filenames[index] || "filename";
|
||||||
return `${prefix}${originalName}${suffix}`;
|
return `${prefix}${baseName}${suffix}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConvertAndDownload = async () => {
|
const handleConvertAndDownload = async () => {
|
||||||
@@ -319,11 +325,16 @@ export function ImageConverter() {
|
|||||||
<div key={url} className="p-4 border rounded-lg flex items-center gap-4">
|
<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" />
|
<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">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm font-medium truncate text-gray-700 dark:text-gray-300" title={images[index].name}>
|
<Label htmlFor={`filename-${index}`} className="text-xs text-muted-foreground">Base Name</Label>
|
||||||
{images[index].name}
|
<Input
|
||||||
</p>
|
id={`filename-${index}`}
|
||||||
<p className="text-xs text-muted-foreground truncate" title={`${generateFinalFilename(index)}_${width || 'w'}x${height || 'h'}.${format}`}>
|
value={filenames[index]}
|
||||||
New name: {generateFinalFilename(index)}
|
onChange={(e) => handleFilenameChange(index, e.target.value)}
|
||||||
|
disabled={useCounter}
|
||||||
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="ghost" size="icon" className="shrink-0 text-gray-500 hover:text-destructive" onClick={() => handleRemoveImage(index)}>
|
<Button variant="ghost" size="icon" className="shrink-0 text-gray-500 hover:text-destructive" onClick={() => handleRemoveImage(index)}>
|
||||||
|
|||||||
Reference in New Issue
Block a user