import { History, ImagePlus, Loader2, Sparkles, Trash2, X, } from 'lucide-react'; import { type ReactNode, useEffect, useRef, useState } from 'react'; import { ResolvedAssetImage } from '../ResolvedAssetImage'; export type CreativeImageInputReferenceImage = { id: string; label: string; imageSrc: string; assetObjectId?: string | null; }; export type CreativeImageInputPanelLabels = { imageField: string; uploadImage: string; replaceImage: string; emptyImageHint: string; removeImage: string; removeImageConfirmTitle: string; removeImageConfirmBody: string; promptReferenceUpload: string; promptReferencePreviewAlt: string; closePromptReferencePreview: string; previewMainImage?: string; closeMainImagePreview?: string; history?: string; }; export type CreativeImageInputPanelProps = { className?: string; fillHeight?: boolean; disabled?: boolean; isSubmitting?: boolean; mainImageMode?: 'edit' | 'preview'; mainImageClickMode?: 'upload' | 'preview'; canUploadMainImage?: boolean; canUseImageHistory?: boolean; canRemoveMainImage?: boolean; canToggleAiRedraw?: boolean; canUploadPromptReferences?: boolean; uploadedImageSrc: string; uploadedImageAlt: string; uploadedImageRefreshKey?: string | number | null; mainImageMeta?: ReactNode; mainImageInputId: string; mainImageAccept?: string; promptTextareaId: string; prompt: string; promptLabel: string; promptAriaLabel?: string; promptRows?: number; aiRedraw: boolean; promptReferenceImages: CreativeImageInputReferenceImage[]; promptReferenceLimit?: number; imageLimitHint?: string | null; imageModelPicker?: ReactNode; error?: string | null; inputError?: string | null; showSubmitButton?: boolean; submitLabel: string; submitCostLabel?: string | null; submitDisabled: boolean; labels: CreativeImageInputPanelLabels; onMainImageFileSelect: (file: File) => void; onMainImageRemove: () => void; onAiRedrawChange: (enabled: boolean) => void; onPromptChange: (value: string) => void; onPromptReferenceFilesSelect?: (files: File[]) => void; onPromptReferenceRemove?: (referenceId: string) => void; onHistoryClick?: () => void; onSubmit: () => void; }; const DEFAULT_IMAGE_ACCEPT = 'image/png,image/jpeg,image/webp'; const DEFAULT_PROMPT_REFERENCE_LIMIT = 5; export function CreativeImageInputPanel({ className = '', fillHeight = true, disabled = false, isSubmitting = false, mainImageMode = 'edit', mainImageClickMode = 'preview', canUploadMainImage = true, canUseImageHistory = true, canRemoveMainImage = true, canToggleAiRedraw = true, canUploadPromptReferences, uploadedImageSrc, uploadedImageAlt, uploadedImageRefreshKey = null, mainImageMeta = null, mainImageInputId, mainImageAccept = DEFAULT_IMAGE_ACCEPT, promptTextareaId, prompt, promptLabel, promptAriaLabel, promptRows = 2, aiRedraw, promptReferenceImages, promptReferenceLimit = DEFAULT_PROMPT_REFERENCE_LIMIT, imageLimitHint = null, imageModelPicker = null, error = null, inputError = null, showSubmitButton = true, submitLabel, submitCostLabel = null, submitDisabled, labels, onMainImageFileSelect, onMainImageRemove, onAiRedrawChange, onPromptChange, onPromptReferenceFilesSelect, onPromptReferenceRemove, onHistoryClick, onSubmit, }: CreativeImageInputPanelProps) { const mainImageInputRef = useRef(null); const [previewReferenceImage, setPreviewReferenceImage] = useState(null); const [isMainImagePreviewOpen, setIsMainImagePreviewOpen] = useState(false); const [isRemoveImageConfirmOpen, setIsRemoveImageConfirmOpen] = useState(false); const showPrompt = mainImageMode === 'preview' || !uploadedImageSrc || aiRedraw; const shouldShowPromptReferences = canUploadPromptReferences ?? !uploadedImageSrc; const promptReferenceUploadDisabled = disabled || promptReferenceImages.length >= promptReferenceLimit; const canEditMainImage = mainImageMode === 'edit'; const isMainImageUploadEnabled = canEditMainImage && canUploadMainImage; const shouldShowHistoryButton = canEditMainImage && canUseImageHistory && Boolean(onHistoryClick); const shouldPreviewMainImage = mainImageClickMode === 'preview' && Boolean(uploadedImageSrc); const shouldShowMainImageUploadButton = isMainImageUploadEnabled && shouldPreviewMainImage; useEffect(() => { if (uploadedImageSrc) { setPreviewReferenceImage(null); } else { setIsMainImagePreviewOpen(false); } }, [uploadedImageSrc]); useEffect(() => { if ( previewReferenceImage && !promptReferenceImages.some( (reference) => reference.id === previewReferenceImage.id, ) ) { setPreviewReferenceImage(null); } }, [previewReferenceImage, promptReferenceImages]); const bodyClassName = fillHeight ? 'creative-image-input-panel__body puzzle-creation-form-body flex min-h-0 flex-1 flex-col overflow-hidden pr-0 lg:overflow-y-auto lg:pr-1' : 'creative-image-input-panel__body puzzle-creation-form-body flex flex-none flex-col overflow-visible pr-0 lg:pr-1'; const sectionClassName = fillHeight ? 'creative-image-input-panel__section puzzle-creation-form-section flex min-h-0 flex-1 flex-col overflow-hidden lg:overflow-visible' : 'creative-image-input-panel__section puzzle-creation-form-section flex flex-none flex-col overflow-visible'; const gridSizeClassName = fillHeight ? 'min-h-0 flex-1' : 'flex-none'; const imageFieldClassName = fillHeight ? 'creative-image-input-panel__image-field puzzle-image-field flex min-h-0 min-w-0 flex-1 flex-col' : 'creative-image-input-panel__image-field puzzle-image-field flex min-w-0 flex-none flex-col'; const imageFrameClassName = fillHeight ? 'creative-image-input-panel__image-frame puzzle-image-card-frame flex min-h-0 flex-1 items-center justify-center' : 'creative-image-input-panel__image-frame puzzle-image-card-frame flex flex-none items-center justify-center'; const imageCardClassName = fillHeight ? 'creative-image-input-panel__image-card puzzle-image-upload-card relative aspect-square h-full min-h-[14rem] max-h-full max-w-full overflow-hidden rounded-[1.25rem] border border-[var(--platform-subpanel-border)] bg-white/90 shadow-[0_12px_28px_rgba(15,23,42,0.08)] transition sm:min-h-[18rem] lg:h-auto lg:w-full' : 'creative-image-input-panel__image-card puzzle-image-upload-card relative aspect-square w-full min-h-[14rem] max-w-full overflow-hidden rounded-[1.25rem] border border-[var(--platform-subpanel-border)] bg-white/90 shadow-[0_12px_28px_rgba(15,23,42,0.08)] transition sm:min-h-[18rem]'; return (
{labels.imageField}
{isMainImageUploadEnabled ? ( { const file = event.currentTarget.files?.[0] ?? null; event.currentTarget.value = ''; if (file) { onMainImageFileSelect(file); } }} className="sr-only" /> ) : null} {shouldPreviewMainImage ? ( ) : null} {shouldShowHistoryButton ? ( ) : null} {canEditMainImage && uploadedImageSrc && canToggleAiRedraw ? ( ) : null} {canEditMainImage && uploadedImageSrc && canRemoveMainImage ? ( ) : isMainImageUploadEnabled && !uploadedImageSrc ? ( ) : null}
{mainImageMeta ?
{mainImageMeta}
: null} {imageLimitHint ? (
{imageLimitHint}
) : null}
{showPrompt ? (