feat: brain file upload button — PDFs, images, text files

- 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) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-01 19:03:52 -05:00
parent 3264aad614
commit 7a5c3382d3

View File

@@ -37,6 +37,8 @@
// Capture
let captureInput = $state('');
let capturing = $state(false);
let uploading = $state(false);
let fileInput: HTMLInputElement;
// Detail
let selectedItem = $state<BrainItem | null>(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}
/>
<button class="upload-btn" onclick={() => fileInput?.click()} disabled={uploading} title="Upload file">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
</button>
{#if captureInput.trim()}
<button class="capture-btn" onclick={capture} disabled={capturing}>
{capturing ? 'Saving...' : 'Save'}
</button>
{/if}
</div>
<input bind:this={fileInput} type="file" class="hidden-input" accept=".pdf,.png,.jpg,.jpeg,.gif,.webp,.txt,.md,.csv" onchange={uploadFile} multiple />
{#if uploading}
<div class="upload-status">Uploading...</div>
{/if}
</section>
<!-- ═══ Folder signal strip ═══ -->
@@ -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 {