feat: support multiple image uploads for AI event generation 🖼️
- Updated OpenRouter integration to accept an array of image URLs - Updated ImagePicker to use the `multiple` attribute natively - Added `appendImagesDeduped` for handling client-side image deduplication - Enhanced clipboard pasting to extract multiple images at once - Rendered multiple images in a horizontal thumbnail strip in the AIToolbar - Added tests to cover multi-image logic and AI request mapping
This commit is contained in:
@@ -5,23 +5,28 @@ import { ImageIcon } from "lucide-react";
|
||||
import type React from "react";
|
||||
import { useImperativeHandle, useRef } from "react";
|
||||
import { Button, type buttonVariants } from "@/components/ui/button";
|
||||
import { IMAGE_ACCEPT } from "@/lib/constants";
|
||||
|
||||
interface ImagePickerProps extends VariantProps<typeof buttonVariants> {
|
||||
onFileSelect?: (file: File) => void;
|
||||
/** Called with ALL selected files (array of 1..N). */
|
||||
onFilesSelect?: (files: File[]) => void;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
/** Allow selecting multiple images at once (native OS picker multi-select). */
|
||||
multiple?: boolean;
|
||||
/** Expose an imperative trigger so parents can open the file dialog via ref */
|
||||
triggerRef?: React.Ref<{ open: () => void }>;
|
||||
}
|
||||
|
||||
export function ImagePicker({
|
||||
onFileSelect,
|
||||
onFilesSelect,
|
||||
className,
|
||||
children,
|
||||
variant = "ghost",
|
||||
size = "icon",
|
||||
disabled = false,
|
||||
multiple = false,
|
||||
triggerRef,
|
||||
}: ImagePickerProps) {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
@@ -38,10 +43,11 @@ export function ImagePicker({
|
||||
};
|
||||
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file && onFileSelect) {
|
||||
onFileSelect(file);
|
||||
const fileList = event.target.files;
|
||||
if (fileList && fileList.length > 0 && onFilesSelect) {
|
||||
onFilesSelect(Array.from(fileList));
|
||||
}
|
||||
// Reset so the same file(s) can be re-selected
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = "";
|
||||
}
|
||||
@@ -53,7 +59,8 @@ export function ImagePicker({
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
name="image-upload"
|
||||
accept="image/png,image/jpeg,image/webp"
|
||||
accept={IMAGE_ACCEPT}
|
||||
multiple={multiple}
|
||||
onChange={handleFileChange}
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user