From 7a5c3382d34b6ad0ce8add6b48ed06e8ce0adba2 Mon Sep 17 00:00:00 2001 From: Yusuf Suleman Date: Wed, 1 Apr 2026 19:03:52 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20brain=20file=20upload=20button=20?= =?UTF-8?q?=E2=80=94=20PDFs,=20images,=20text=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Upload button (arrow icon) in capture bar next to text input - Accepts: PDF, PNG, JPG, GIF, WEBP, TXT, MD, CSV - Multiple file upload supported - Hidden file input triggered by button click - Upload status indicator while processing - Files sent to /api/brain/items/upload endpoint Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lib/pages/brain/AtelierBrainPage.svelte | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/frontend-v2/src/lib/pages/brain/AtelierBrainPage.svelte b/frontend-v2/src/lib/pages/brain/AtelierBrainPage.svelte index 4064015..50417f6 100644 --- a/frontend-v2/src/lib/pages/brain/AtelierBrainPage.svelte +++ b/frontend-v2/src/lib/pages/brain/AtelierBrainPage.svelte @@ -37,6 +37,8 @@ // Capture let captureInput = $state(''); let capturing = $state(false); + let uploading = $state(false); + let fileInput: HTMLInputElement; // Detail let selectedItem = $state(null); @@ -119,6 +121,26 @@ searching = false; } + async function uploadFile(e: Event) { + const input = e.target as HTMLInputElement; + if (!input.files?.length) return; + uploading = true; + try { + for (const file of input.files) { + const formData = new FormData(); + formData.append('file', file); + await fetch('/api/brain/items/upload', { + method: 'POST', + credentials: 'include', + body: formData, + }); + } + input.value = ''; + await loadItems(); + } catch { /* silent */ } + uploading = false; + } + function startEditNote() { if (!selectedItem) return; editNoteContent = selectedItem.raw_content || ''; @@ -254,12 +276,19 @@ onkeydown={handleCaptureKey} disabled={capturing} /> + {#if captureInput.trim()} {/if} + + {#if uploading} +
Uploading...
+ {/if} @@ -535,6 +564,26 @@ color: #1e1812; font-size: 1rem; font-family: var(--font); outline: none; } .capture-input::placeholder { color: #8b7b6a; } + .upload-btn { + flex-shrink: 0; + width: 36px; height: 36px; + border-radius: 10px; + border: 1px solid rgba(35,26,17,0.1); + background: rgba(255,255,255,0.6); + color: #7f7365; + display: flex; align-items: center; justify-content: center; + transition: all 160ms; + } + .upload-btn:hover { background: rgba(255,255,255,0.9); color: #1e1812; border-color: rgba(35,26,17,0.2); } + .upload-btn:active { transform: scale(0.95); } + + .hidden-input { display: none; } + + .upload-status { + font-size: 0.82rem; color: #8c7b69; + margin-top: 8px; padding-left: 28px; + } + .capture-btn { padding: 8px 18px; border-radius: 999px; background: #1e1812; color: white; border: none; @@ -640,6 +689,7 @@ height: auto; display: block; object-fit: cover; + object-position: top; max-height: 240px; } .card-type-badge {