feat: chat stop button, image paste, vision AI, search fixes
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 43s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 43s
- Add stop button to all chat interfaces (floating, contextual, full-page) - Add conversation sliding window (50 messages) to prevent context overflow - Add chat timeout warning (30s toast) - Force response language in chat system prompt (mandatory per-locale) - Add image paste from clipboard in all note editors (card, list, input) - Fix upload API to infer extension from MIME type for clipboard images - Add image description support in note AI chat (base64 vision) - Fix search regex crash on special characters (escape user input) - Fix search case-insensitivity on PostgreSQL (mode: 'insensitive') - Add try/catch around semantic search in chat route (prevent blocking) - Add new chat button to floating AI assistant - Fix empty thinking bubbles for reasoning models (filter non-text parts) - Remove duplicate AI assistant toggle from note editor header - Improve link metadata scraping (timeout, content-type check, relative URLs) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -434,6 +434,38 @@ export function NoteInput({
|
||||
e.target.value = ''
|
||||
}
|
||||
|
||||
const uploadImageFile = async (file: File) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const response = await fetch('/api/upload', { method: 'POST', body: formData })
|
||||
if (!response.ok) throw new Error('Upload failed')
|
||||
const data = await response.json()
|
||||
return data.url
|
||||
}
|
||||
|
||||
// Paste handler: upload clipboard images
|
||||
useEffect(() => {
|
||||
const handlePaste = async (e: ClipboardEvent) => {
|
||||
const items = e.clipboardData?.items
|
||||
if (!items) return
|
||||
for (const item of Array.from(items)) {
|
||||
if (item.type.startsWith('image/')) {
|
||||
e.preventDefault()
|
||||
const file = item.getAsFile()
|
||||
if (!file) continue
|
||||
try {
|
||||
const url = await uploadImageFile(file)
|
||||
setImages(prev => [...prev, url])
|
||||
} catch {
|
||||
toast.error(t('notes.uploadFailed', { filename: 'pasted image' }))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
document.addEventListener('paste', handlePaste)
|
||||
return () => document.removeEventListener('paste', handlePaste)
|
||||
}, [t])
|
||||
|
||||
const handleAddLink = async () => {
|
||||
if (!linkUrl) return
|
||||
|
||||
@@ -927,6 +959,7 @@ export function NoteInput({
|
||||
onClose={() => setAiOpen(false)}
|
||||
noteTitle={title}
|
||||
noteContent={content}
|
||||
noteImages={images}
|
||||
onApplyToNote={(newContent) => setContent(newContent)}
|
||||
lastActionApplied={false}
|
||||
notebooks={notebooks.map(nb => ({ id: nb.id, name: nb.name }))}
|
||||
|
||||
Reference in New Issue
Block a user