mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-04 05:40:30 +00:00
- Added attachment draft/shared types and validation in components/chat/attachments.ts. - Added paperclip upload UI to Nova composer with file chips, size/status, remove, retry, and per-file Save / Chat only toggle. - Wired uploads before send to /chat/attachments, then sends returned attachment references in chat message metadata. - Preserved attachment metadata when loading threads and rendered attachment chips on user messages. - Added attachment support from the home composer into the full chat view. - Extended chat analytics with attachment counts. <img width="1905" height="900" alt="image" src="https://github.com/user-attachments/assets/631001b8-7c68-4015-b36b-06c69cdad271" /> <img width="1323" height="1600" alt="image" src="https://github.com/user-attachments/assets/77eee08a-b235-41eb-b03a-f406b81b46e7" /> - ensured responsiveness
82 lines
2 KiB
TypeScript
82 lines
2 KiB
TypeScript
export const CHAT_ATTACHMENT_ACCEPT =
|
|
"image/*,.pdf,application/pdf,.doc,.docx,.txt,.md,.mdx,.markdown,text/markdown"
|
|
|
|
export const CHAT_ATTACHMENT_MAX_BYTES = 50 * 1024 * 1024
|
|
|
|
const SUPPORTED_EXTENSIONS = new Set([
|
|
".pdf",
|
|
".doc",
|
|
".docx",
|
|
".txt",
|
|
".md",
|
|
".mdx",
|
|
".markdown",
|
|
])
|
|
|
|
export type ChatAttachment = {
|
|
id: string
|
|
documentId?: string
|
|
filename: string
|
|
mediaType: string
|
|
size: number
|
|
saveToMemory: boolean
|
|
status: "ready" | "processing" | "failed"
|
|
url?: string
|
|
contentPreview?: string
|
|
}
|
|
|
|
export type ChatAttachmentDraftStatus =
|
|
| "queued"
|
|
| "uploading"
|
|
| "uploaded"
|
|
| "error"
|
|
|
|
export type ChatAttachmentDraft = {
|
|
id: string
|
|
file: File
|
|
saveToMemory: boolean
|
|
status: ChatAttachmentDraftStatus
|
|
errorMessage?: string
|
|
uploaded?: ChatAttachment
|
|
}
|
|
|
|
export type ChatAttachmentMessageMetadata = {
|
|
attachments?: ChatAttachment[]
|
|
}
|
|
|
|
export function isAcceptedChatAttachment(file: File): boolean {
|
|
if (file.size > CHAT_ATTACHMENT_MAX_BYTES) return false
|
|
const name = file.name.toLowerCase()
|
|
const ext = name.includes(".") ? name.slice(name.lastIndexOf(".")) : ""
|
|
if (SUPPORTED_EXTENSIONS.has(ext)) return true
|
|
if (file.type.startsWith("image/")) return true
|
|
if (file.type === "application/pdf") return true
|
|
if (file.type === "text/markdown") return true
|
|
return false
|
|
}
|
|
|
|
export function chatAttachmentKey(file: File): string {
|
|
return `${file.name}:${file.size}:${file.lastModified}`
|
|
}
|
|
|
|
export function createChatAttachmentDraft(file: File): ChatAttachmentDraft {
|
|
return {
|
|
id: crypto.randomUUID(),
|
|
file,
|
|
saveToMemory: true,
|
|
status: "queued",
|
|
}
|
|
}
|
|
|
|
export function formatAttachmentSize(size: number): string {
|
|
if (size < 1024) return `${size} B`
|
|
const kb = size / 1024
|
|
if (kb < 1024) return `${kb.toFixed(1)} KB`
|
|
return `${(kb / 1024).toFixed(1)} MB`
|
|
}
|
|
|
|
export function getChatMessageAttachments(metadata: unknown): ChatAttachment[] {
|
|
const attachments = (metadata as ChatAttachmentMessageMetadata | undefined)
|
|
?.attachments
|
|
return Array.isArray(attachments) ? attachments : []
|
|
}
|