"use client"; import { motion } from "motion/react"; import { AnimatePresence } from "motion/react"; import { createContext, memo, useContext, useMemo } from "react"; import { useState } from "react"; import { useDropzone } from "react-dropzone"; import { toast } from "sonner"; import { useTranslation } from "@turbostarter/i18n"; import { cn } from "@turbostarter/ui"; import { Avatar, AvatarFallback, AvatarImage, } from "@turbostarter/ui-web/avatar"; import { Button } from "@turbostarter/ui-web/button"; import { Icons } from "@turbostarter/ui-web/icons"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@turbostarter/ui-web/tooltip"; import { Viewer } from "~/modules/common/image"; import type { DropzoneOptions, DropzoneState } from "react-dropzone"; const DropzoneContext = createContext<{ dropzone: DropzoneState; } | null>(null); interface DropzoneProps extends DropzoneOptions { children: React.ReactNode; dialog?: React.ReactNode; } const Dropzone = ({ children, dialog, ...options }: DropzoneProps) => { const dropzone = useDropzone({ accept: { "image/*": [".png", ".gif", ".jpeg", ".webp", ".jpg"], }, onError: (error) => toast.error(error.message), noClick: true, noKeyboard: true, multiple: true, ...options, }); return (
{children} {dropzone.isDragActive && dialog && (
{dialog}
)}
); }; const Input = memo>((props) => { const { t } = useTranslation(["ai", "common"]); const context = useContext(DropzoneContext); return ( <> {t("chat.composer.files.add")} ); }); Input.displayName = "Input"; interface PreviewProps extends React.HTMLAttributes { attachments: File[]; onRemove: (file: File) => void; } export const Preview = memo( ({ attachments, onRemove, className, ...props }) => { const [isOpen, setIsOpen] = useState(false); const [selectedImage, setSelectedImage] = useState(0); if (!attachments.length) { return null; } return ( <>
{attachments.map((attachment, index) => ( onRemove(attachment)} onClick={() => { setSelectedImage(index); setIsOpen(true); }} /> ))}
({ url: URL.createObjectURL(attachment), }))} selectedImage={selectedImage} setSelectedImage={setSelectedImage} /> ); }, ); Preview.displayName = "Preview"; interface ThumbnailProps extends React.HTMLAttributes { attachment: File; onRemove: () => void; } const Thumbnail = memo(({ attachment, onRemove, ...props }) => { const { t } = useTranslation(["ai"]); const preview = useMemo(() => URL.createObjectURL(attachment), [attachment]); return (
{t("chat.composer.files.remove")}
); }); Thumbnail.displayName = "Thumbnail"; export const Attachments = { Input, Dropzone, Preview, };