diff --git a/.vscode/settings.json b/.vscode/settings.json index 0c71db549..7d33f678b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,4 +19,4 @@ "titleBar.inactiveForeground": "#e7e7e799" }, "peacock.color": "#38b" -} \ No newline at end of file +} diff --git a/vibes.diy/pkg/app/components/ChatInput.tsx b/vibes.diy/pkg/app/components/ChatInput.tsx index 897a8afd9..f03e03342 100644 --- a/vibes.diy/pkg/app/components/ChatInput.tsx +++ b/vibes.diy/pkg/app/components/ChatInput.tsx @@ -9,6 +9,7 @@ import React, { } from "react"; import type { ChatState } from "../types/chat.js"; import ModelPicker, { type ModelOption } from "./ModelPicker.js"; +import ImagePreview from "./ImagePreview.js"; import { preloadLlmsText } from "../prompts.js"; interface ChatInputProps { @@ -45,6 +46,8 @@ const ChatInput = forwardRef( // State for responsive behavior const [isCompact, setIsCompact] = useState(false); + const [isDragging, setIsDragging] = useState(false); + const fileInputRef = useRef(null); // Expose the click function to parent components useImperativeHandle( @@ -102,9 +105,64 @@ const ChatInput = forwardRef( }; }, []); + const handleFilesSelected = useCallback( + async (files: FileList | null) => { + if (!files || !chatState.attachImages) return; + await chatState.attachImages(files); + }, + [chatState.attachImages], + ); + + const onDropFiles = useCallback( + async (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragging(false); + const files = e.dataTransfer?.files; + if (files && chatState.attachImages) { + await chatState.attachImages(files); + } + }, + [chatState.attachImages], + ); + return ( -
-
+
{ + e.preventDefault(); + setIsDragging(true); + }} + onDragOver={(e) => { + // Prevent default to avoid the browser opening the file + e.preventDefault(); + setIsDragging(true); + }} + onDragLeave={(e) => { + e.preventDefault(); + setIsDragging(false); + }} + onDrop={(e) => { + // Prevent navigation and route valid file drops to the shared handler + e.preventDefault(); + e.stopPropagation(); + setIsDragging(false); + if (e.dataTransfer?.files?.length) { + void onDropFiles(e); + } + }} + > +
+ {/* Attached images preview */} + {Array.isArray(chatState.attachedImages) && + chatState.attachedImages.length > 0 && ( + chatState.removeAttachedImage?.(id)} + /> + )} +