diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index c2e37e483..c5dcb9c1b 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -35,7 +35,8 @@ "effects": ["fullScreenUI", "mica", "tabbed", "blur", "acrylic"], "state": "active", "radius": 8 - } + }, + "dragDropEnabled": false } ], "security": { diff --git a/web-app/src/containers/ChatInput.tsx b/web-app/src/containers/ChatInput.tsx index 59cdaa3cd..dc3935ff6 100644 --- a/web-app/src/containers/ChatInput.tsx +++ b/web-app/src/containers/ChatInput.tsx @@ -70,6 +70,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => { }> >([]) const [connectedServers, setConnectedServers] = useState([]) + const [isDragOver, setIsDragOver] = useState(false) // Check for connected MCP servers useEffect(() => { @@ -281,6 +282,54 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => { } } + const handleDragEnter = (e: React.DragEvent) => { + e.preventDefault() + e.stopPropagation() + setIsDragOver(true) + } + + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault() + e.stopPropagation() + // Only set dragOver to false if we're leaving the drop zone entirely + // In Tauri, relatedTarget can be null, so we need to handle that case + const relatedTarget = e.relatedTarget as Node | null + if (!relatedTarget || !e.currentTarget.contains(relatedTarget)) { + setIsDragOver(false) + } + } + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault() + e.stopPropagation() + // Ensure drag state is maintained during drag over + setIsDragOver(true) + } + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault() + e.stopPropagation() + setIsDragOver(false) + + // Check if dataTransfer exists (it might not in some Tauri scenarios) + if (!e.dataTransfer) { + console.warn('No dataTransfer available in drop event') + return + } + + const files = e.dataTransfer.files + if (files && files.length > 0) { + // Create a synthetic event to reuse existing file handling logic + const syntheticEvent = { + target: { + files: files, + }, + } as React.ChangeEvent + + handleFileChange(syntheticEvent) + } + } + return (
@@ -305,8 +354,13 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
{uploadedFiles.length > 0 && (
@@ -372,8 +426,14 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => { }} onKeyDown={(e) => { // e.keyCode 229 is for IME input with Safari - const isComposing = e.nativeEvent.isComposing || e.keyCode === 229; - if (e.key === 'Enter' && !e.shiftKey && prompt.trim() && !isComposing) { + const isComposing = + e.nativeEvent.isComposing || e.keyCode === 229 + if ( + e.key === 'Enter' && + !e.shiftKey && + prompt.trim() && + !isComposing + ) { e.preventDefault() // Submit the message when Enter is pressed without Shift handleSendMesage(prompt) @@ -414,7 +474,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => { )} {/* File attachment - always available */}
@@ -422,6 +482,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => { type="file" ref={fileInputRef} className="hidden" + multiple onChange={handleFileChange} />