From e950484af6c2bae99035cf1f2dde669c0126ecee Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sat, 14 Mar 2026 22:22:35 +0800 Subject: [PATCH] feat(home): add drag file to chat input, add settings config API --- api/core/settings/routes.ts | 9 +++++ src/components/chat/Chat.tsx | 47 +++++++++++++++++++++++++ src/components/chat/Greeting.tsx | 2 +- src/components/chat/MultimodalInput.tsx | 12 +++++++ src/lib/api/client.ts | 4 +++ src/lib/api/index.ts | 1 + 6 files changed, 74 insertions(+), 1 deletion(-) diff --git a/api/core/settings/routes.ts b/api/core/settings/routes.ts index d58fd55..74b2076 100644 --- a/api/core/settings/routes.ts +++ b/api/core/settings/routes.ts @@ -51,4 +51,13 @@ router.post( }), ) +router.get( + '/config', + asyncHandler(async (req: Request, res: Response) => { + successResponse(res, { + notebookRoot: NOTEBOOK_ROOT, + }) + }), +) + export default router diff --git a/src/components/chat/Chat.tsx b/src/components/chat/Chat.tsx index e5880d7..7800240 100644 --- a/src/components/chat/Chat.tsx +++ b/src/components/chat/Chat.tsx @@ -13,6 +13,8 @@ import { Messages } from './Messages' import { MultimodalInput } from './MultimodalInput' import { generateUUID } from '@/lib/utils' import { Eraser } from 'lucide-react' +import { useDragStore } from '@/stores/dragStore' +import { getSettingsConfig } from '@/lib/api' type Status = 'submitted' | 'streaming' | 'ready' | 'error' @@ -51,6 +53,49 @@ export function Chat({ const [status, setStatus] = useState('ready') const [attachments, setAttachments] = useState([]) const [showInputAtBottom, setShowInputAtBottom] = useState(false) + const [notebookRoot, setNotebookRoot] = useState('') + const { state: dragState } = useDragStore() + + useEffect(() => { + getSettingsConfig().then(config => { + // 统一 notebookRoot 的斜杠 + const normalizedRoot = (config.notebookRoot || '').replace(/\\/g, '/') + setNotebookRoot(normalizedRoot) + }).catch(e => { + console.error('Failed to get notebook root:', e) + }) + }, []) + + const handleDrop = useCallback((e: React.DragEvent) => { + e.preventDefault() + e.stopPropagation() + + // 优先从 dragStore 获取 + let { draggedPath, draggedType } = dragState + + // 如果 dragStore 为空,尝试从 dataTransfer 获取 + if (!draggedPath) { + draggedPath = e.dataTransfer.getData('text/plain') + } + + if (draggedPath) { + // 统一路径中的斜杠,并拼接完整路径,用【】包围 + const normalizedPath = draggedPath.replace(/\\/g, '/') + const fullPath = notebookRoot ? `${notebookRoot}/${normalizedPath}` : normalizedPath + const pathWithBrackets = `【${fullPath}】` + setInput(prev => prev + pathWithBrackets) + + // 延迟聚焦输入框 + setTimeout(() => { + const textarea = document.querySelector('textarea') as HTMLTextAreaElement + textarea?.focus() + }, 0) + } + }, [dragState, notebookRoot]) + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault() + }, []) const scrollToBottom = useCallback(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'auto' }) @@ -211,6 +256,8 @@ export function Chat({ setInput={setInput} status={status} stop={handleStop} + onDrop={handleDrop} + onDragOver={handleDragOver} /> diff --git a/src/components/chat/Greeting.tsx b/src/components/chat/Greeting.tsx index 2442c3a..0910081 100644 --- a/src/components/chat/Greeting.tsx +++ b/src/components/chat/Greeting.tsx @@ -1,6 +1,6 @@ export function Greeting() { return ( -
+

开始今天的工作吧!

diff --git a/src/components/chat/MultimodalInput.tsx b/src/components/chat/MultimodalInput.tsx index c37839a..651865d 100644 --- a/src/components/chat/MultimodalInput.tsx +++ b/src/components/chat/MultimodalInput.tsx @@ -30,6 +30,8 @@ interface MultimodalInputProps { messages: ChatMessage[] sendMessage: (text: string, attachments: Attachment[]) => Promise className?: string + onDrop?: (e: React.DragEvent) => void + onDragOver?: (e: React.DragEvent) => void } export function MultimodalInput({ @@ -42,6 +44,8 @@ export function MultimodalInput({ setAttachments, sendMessage, className, + onDrop, + onDragOver, }: MultimodalInputProps) { const textareaRef = useRef(null) @@ -135,6 +139,14 @@ export function MultimodalInput({ value={input} onChange={handleInput} onKeyDown={handleKeyDown} + onDrop={(e) => { + e.preventDefault() + onDrop?.(e) + }} + onDragOver={(e) => { + e.preventDefault() + onDragOver?.(e) + }} placeholder="发送消息..." rows={1} /> diff --git a/src/lib/api/client.ts b/src/lib/api/client.ts index 1295cda..441dc9f 100644 --- a/src/lib/api/client.ts +++ b/src/lib/api/client.ts @@ -83,6 +83,10 @@ export const getSettings = async (): Promise => { return await fetchApi('/api/settings') } +export const getSettingsConfig = async (): Promise<{ notebookRoot: string }> => { + return await fetchApi<{ notebookRoot: string }>('/api/settings/config') +} + export const saveSettings = async (settings: Settings): Promise => { return await fetchApi('/api/settings', { method: 'POST', diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 9bd82a3..55ef25d 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -13,6 +13,7 @@ export { renameItem, runAiTask, getSettings, + getSettingsConfig, saveSettings, uploadPdfForParsing, parseLocalHtml,