Add drag-and-drop functionality for chat attachments and update message sending logic

This commit is contained in:
2026-04-16 21:31:04 +02:00
parent 972263d523
commit 2e116b8701

View File

@@ -1457,9 +1457,52 @@ async function regenerateFromIndex(index, overrideUserText = null) {
} }
}; };
const handleChatDragEnter = (event) => {
if (activeSidebarMode !== 'chats' || !hasFilePayload(event)) return
event.preventDefault()
imageDragDepthRef.current += 1
if (selectedModelSupportsVision && eventHasImageFiles(event)) {
setIsChatDragActive(true)
}
}
const handleChatDragOver = (event) => {
if (activeSidebarMode !== 'chats' || !hasFilePayload(event)) return
event.preventDefault()
event.dataTransfer.dropEffect = selectedModelSupportsVision ? 'copy' : 'none'
if (selectedModelSupportsVision && eventHasImageFiles(event) && !isChatDragActive) {
setIsChatDragActive(true)
}
}
const handleChatDragLeave = (event) => {
if (activeSidebarMode !== 'chats' || !hasFilePayload(event)) return
imageDragDepthRef.current = Math.max(0, imageDragDepthRef.current - 1)
if (imageDragDepthRef.current === 0) {
setIsChatDragActive(false)
}
}
const handleChatDrop = async (event) => {
if (activeSidebarMode !== 'chats' || !hasFilePayload(event)) return
event.preventDefault()
imageDragDepthRef.current = 0
setIsChatDragActive(false)
if (!selectedModelSupportsVision) {
return
}
await appendComposerImageFiles(event.dataTransfer?.files)
textareaRef.current?.focus()
}
async function sendMessage() { async function sendMessage() {
if (isSending || !input.trim() || !model) return const trimmedInput = input.trim()
if (isSending || (!trimmedInput && composerAttachments.length === 0) || !model) return
if (composerAttachments.length > 0 && !selectedModelSupportsVision) {
window.alert('The selected model does not support image inputs.')
return
}
let targetSessionId = activeSessionId let targetSessionId = activeSessionId
let isNewChat = false let isNewChat = false
@@ -1473,7 +1516,13 @@ async function sendMessage() {
isNewChat = currentSession && currentSession.name === "New Chat" && currentSession.messages.length === 0 isNewChat = currentSession && currentSession.name === "New Chat" && currentSession.messages.length === 0
} }
const userMsg = { role: 'user', content: input.trim(), id: `msg-${Date.now()}-${Math.random()}` } const outgoingAttachments = composerAttachments.map(({ id, ...attachment }) => ({ ...attachment }))
const userMsg = {
role: 'user',
content: trimmedInput,
attachments: outgoingAttachments,
id: `msg-${Date.now()}-${Math.random()}`
}
justSentMessage.current = true justSentMessage.current = true
lastSentSessionRef.current = targetSessionId lastSentSessionRef.current = targetSessionId
setUserScrolledUp(targetSessionId, false) setUserScrolledUp(targetSessionId, false)
@@ -1491,25 +1540,26 @@ async function sendMessage() {
) )
) )
setInput('') setInput('')
setComposerAttachments([])
}) })
requestAnimationFrame(() => scrollToBottom('auto', targetSessionId)) requestAnimationFrame(() => scrollToBottom('auto', targetSessionId))
const requestController = beginCancelableRequest(targetSessionId) const requestController = beginCancelableRequest(targetSessionId)
try { try {
let historyForSearch = [] let historyForSearch = []
try { if (userMsg.content) try {
const existing = (chatSessions.find(s => s.session_id === targetSessionId)?.messages) || [] const existing = (chatSessions.find(s => s.session_id === targetSessionId)?.messages) || []
const lastFew = existing.slice(-8).map(m => ({ role: m.role, content: m.content || '' })) const lastFew = existing.slice(-8).map(m => ({ role: m.role, content: m.content || '' }))
historyForSearch = [...lastFew, { role: 'user', content: userMsg.content }] historyForSearch = [...lastFew, { role: 'user', content: userMsg.content }]
} catch {} } catch {}
let enrichedPrompt = userMsg.content let enrichedPrompt = userMsg.content || null
let citationSources = [] let citationSources = []
const contextBlocks = [] const contextBlocks = []
const selectedLibrary = getChatLibraryForSession(targetSessionId) const selectedLibrary = getChatLibraryForSession(targetSessionId)
if (selectedLibrary?.states?.is_indexed) { if (userMsg.content && selectedLibrary?.states?.is_indexed) {
try { try {
const localContext = await fetchLocalLibraryContext(selectedLibrary.slug, userMsg.content, requestController.signal) const localContext = await fetchLocalLibraryContext(selectedLibrary.slug, userMsg.content, requestController.signal)
if (localContext.contextBlock) { if (localContext.contextBlock) {
@@ -1524,7 +1574,7 @@ async function sendMessage() {
} }
} }
if (webSearchEnabled) { if (userMsg.content && webSearchEnabled) {
try { try {
const resp = await fetch(`${backendApiUrl}/websearch`, { const resp = await fetch(`${backendApiUrl}/websearch`, {
method: 'POST', method: 'POST',
@@ -1553,7 +1603,7 @@ async function sendMessage() {
} }
citationSources = [...new Set(citationSources)] citationSources = [...new Set(citationSources)]
if (contextBlocks.length > 0) { if (userMsg.content && contextBlocks.length > 0) {
enrichedPrompt = `${userMsg.content}\n\n${contextBlocks.join('\n\n')}` enrichedPrompt = `${userMsg.content}\n\n${contextBlocks.join('\n\n')}`
} }
@@ -1578,9 +1628,10 @@ async function sendMessage() {
session_id: targetSessionId, session_id: targetSessionId,
model, model,
message: userMsg.content, message: userMsg.content,
enriched_message: contextBlocks.length > 0 ? enrichedPrompt : null, enriched_message: userMsg.content && contextBlocks.length > 0 ? enrichedPrompt : null,
stream: true, stream: true,
sources: citationSources || [] sources: citationSources || [],
attachments: outgoingAttachments,
}) })
}) })
if (!res.ok) throw new Error(`HTTP ${res.status}`) if (!res.ok) throw new Error(`HTTP ${res.status}`)
@@ -1641,9 +1692,10 @@ async function sendMessage() {
session_id: targetSessionId, session_id: targetSessionId,
model, model,
message: userMsg.content, message: userMsg.content,
enriched_message: contextBlocks.length > 0 ? enrichedPrompt : null, enriched_message: userMsg.content && contextBlocks.length > 0 ? enrichedPrompt : null,
stream: false, stream: false,
sources: citationSources || [] sources: citationSources || [],
attachments: outgoingAttachments,
}) })
}) })
if (!res.ok) throw new Error(`HTTP ${res.status}`) if (!res.ok) throw new Error(`HTTP ${res.status}`)
@@ -1688,7 +1740,7 @@ async function sendMessage() {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
session_id: targetSessionId, session_id: targetSessionId,
message: userMsg.content, message: userMsg.content || (outgoingAttachments.length > 0 ? 'Image attachment' : userMsg.content),
model model
}) })
}) })