import { useEffect, useRef, useState } from "react"; import { FileText, Trash2, UploadCloud, X } from "lucide-react"; import { api } from "../lib/api.js"; function formatSize(bytes) { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / 1024 / 1024).toFixed(1)} MB`; } export default function DocumentsPanel({ onClose }) { const [documents, setDocuments] = useState([]); const [uploading, setUploading] = useState(false); const [dragging, setDragging] = useState(false); const [error, setError] = useState(""); const inputRef = useRef(null); async function refresh() { try { setDocuments(await api("/api/documents")); } catch (err) { setError(err.message); } } useEffect(() => { refresh(); }, []); async function upload(files) { setError(""); for (const file of files) { setUploading(true); try { const form = new FormData(); form.append("file", file); await api("/api/documents", { method: "POST", body: form }); await refresh(); } catch (err) { setError(err.message); } finally { setUploading(false); } } } async function remove(id) { try { await api(`/api/documents/${id}`, { method: "DELETE" }); setDocuments((prev) => prev.filter((d) => d.id !== id)); } catch (err) { setError(err.message); } } return ( <>
Knowledge base
inputRef.current?.click()} onDragOver={(e) => { e.preventDefault(); setDragging(true); }} onDragLeave={() => setDragging(false)} onDrop={(e) => { e.preventDefault(); setDragging(false); upload([...e.dataTransfer.files]); }} >
{uploading ? "Uploading and indexing..." : "Drop files here or click to upload"}
PDF, DOCX, TXT or MD, up to 10 MB. Files are chunked, embedded and made searchable to the agent.
{ upload([...e.target.files]); e.target.value = ""; }} />
{error &&
{error}
} {documents.length === 0 && !uploading && (
No documents yet. Upload one and ask Synapse about it.
)} {documents.map((doc) => (
{doc.filename}
{formatSize(doc.size_bytes)} {doc.status === "ready" && ` · ${doc.chunk_count} chunks`} {doc.status === "failed" && doc.error ? ` · ${doc.error.slice(0, 80)}` : ""}
{doc.status}
))}
); }