diff --git a/.vscode/settings.json b/.vscode/settings.json index fa6153c78..01a3e2ec6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,5 +12,11 @@ ], "cSpell.words": [ "Eigent" + ], + "i18n-ally.localesPaths": [ + "backend/lang", + "server/lang", + "src/i18n", + "src/i18n/locales" ] } diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..90d74b3a7 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,30 @@ +# Security Policy + +## Supported Versions + +The following versions of Eigent are currently being supported with security updates: + +| Version | Supported | +| ------- | ------------------ | +| 0.0.x | :white_check_mark: | +| < 0.0 | :x: | + +## Reporting a Vulnerability + +If you discover a security vulnerability in Eigent, please report it responsibly: + +### How to Report +- **Email**: Send details to info@eigent.ai +- **GitHub**: Use GitHub's private security advisory feature +- **Include**: Detailed description, steps to reproduce, and potential impact + +### What to Expect +- **Response Time**: We aim to acknowledge reports within 48 hours +- **Updates**: We will provide updates on the investigation progress weekly +- **Resolution**: Critical vulnerabilities will be addressed within 7 days +- **Credit**: We will credit security researchers in our security advisories (if desired) + +### Security Disclosure Policy +- We follow responsible disclosure practices +- We request 90 days to address the vulnerability before public disclosure +- We will coordinate disclosure timing with the reporter diff --git a/electron/main/init.ts b/electron/main/init.ts index 57d596f3e..d1db6b8a7 100644 --- a/electron/main/init.ts +++ b/electron/main/init.ts @@ -155,7 +155,7 @@ export async function startBackend(setPort?: (port: number) => void): Promise { + const displayFilteredLogs = (data: String) => { if (!data) return; const msg = data.toString().trimEnd(); if (msg.toLowerCase().includes("error") || msg.toLowerCase().includes("traceback")) { diff --git a/src/assets/wechat_qr_1.jpg b/src/assets/wechat_qr_1.jpg index de106ba0c..262368de8 100644 Binary files a/src/assets/wechat_qr_1.jpg and b/src/assets/wechat_qr_1.jpg differ diff --git a/src/assets/wechat_qr_2.jpg b/src/assets/wechat_qr_2.jpg index 186da1a82..10dcd99ea 100644 Binary files a/src/assets/wechat_qr_2.jpg and b/src/assets/wechat_qr_2.jpg differ diff --git a/src/assets/wechat_qr_3.jpg b/src/assets/wechat_qr_3.jpg index 9b852f62d..880356cbe 100644 Binary files a/src/assets/wechat_qr_3.jpg and b/src/assets/wechat_qr_3.jpg differ diff --git a/src/assets/wechat_qr_4.jpg b/src/assets/wechat_qr_4.jpg index 9efee4d64..ccf2ede66 100644 Binary files a/src/assets/wechat_qr_4.jpg and b/src/assets/wechat_qr_4.jpg differ diff --git a/src/components/ChatBox/BottomInput.tsx b/src/components/ChatBox/BottomInput.tsx index 3187667a8..263604d53 100644 --- a/src/components/ChatBox/BottomInput.tsx +++ b/src/components/ChatBox/BottomInput.tsx @@ -13,13 +13,14 @@ import { Play, Image, FileText, + UploadCloud, } from "lucide-react"; import { useChatStore } from "@/store/chatStore"; import racPause from "@/assets/rac-pause.svg"; import { fetchDelete, proxyFetchDelete } from "@/api/http"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { fetchPut } from "@/api/http"; import { Tag } from "../ui/tag"; import { useTranslation } from "react-i18next"; @@ -88,6 +89,8 @@ export const BottomInput = ({ }, [chatStore]); const [isLoading, setIsLoading] = useState(false); + const [isDragging, setIsDragging] = useState(false); + const dragCounter = useRef(0); const handleTakeControl = (type: "pause" | "resume") => { setIsLoading(true); if (type === "pause") { @@ -135,6 +138,64 @@ export const BottomInput = ({ } }; + // drag & drop files + const isFileDrag = (e: React.DragEvent) => { + try { + return Array.from(e.dataTransfer?.types || []).includes("Files"); + } catch { + return false; + } + }; + + const handleDragOver = (e: React.DragEvent) => { + if (!privacy || isPending || useCloudModelInDev) return; + if (!isFileDrag(e)) return; + e.preventDefault(); + e.stopPropagation(); + e.dataTransfer.dropEffect = "copy"; + setIsDragging(true); + }; + + const handleDragEnter = (e: React.DragEvent) => { + if (!privacy || isPending || useCloudModelInDev) return; + if (!isFileDrag(e)) return; + e.preventDefault(); + e.stopPropagation(); + dragCounter.current += 1; + setIsDragging(true); + }; + + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + dragCounter.current = Math.max(0, dragCounter.current - 1); + if (dragCounter.current === 0) setIsDragging(false); + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragging(false); + dragCounter.current = 0; + if (!privacy || isPending || useCloudModelInDev) return; + try { + const dropped = Array.from(e.dataTransfer?.files || []); + if (dropped.length === 0) return; + const current = chatStore.tasks[chatStore.activeTaskId as string].attaches; + const mapped = dropped.map((f: File) => ({ + fileName: f.name, + filePath: (f as any).path || f.name, + })); + const files = [ + ...current.filter((f: File) => !mapped.find((m) => m.filePath === f.filePath)), + ...mapped.filter((m) => !current.find((f) => f.filePath === m.filePath)), + ]; + chatStore.setAttaches(chatStore.activeTaskId as string, files as File[]); + } catch (error) { + console.error("Drop File Error:", error); + } + }; + const handleEditQuery = () => { fetchDelete(`/chat/${chatStore.activeTaskId}`); const tempTaskId = chatStore.activeTaskId; @@ -312,7 +373,22 @@ export const BottomInput = ({ )} ) : ( -
+
+ {isDragging && ( +
+ +
+ Drop files to attach +
+ +
+ )}