From b87a3a583dc6b4640ed6631be31481d4ece2fc81 Mon Sep 17 00:00:00 2001 From: Yusuf Suleman Date: Wed, 1 Apr 2026 21:36:13 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20upload=20button=20in=20AppShell=20sideb?= =?UTF-8?q?ar=20=E2=80=94=20click=20or=20drag=20to=20upload=20screenshots?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dashed border button at bottom of sidebar nav - Click to open file picker - Drag files onto the bottom rail area to upload - Shows "Uploading..." then "Saved!" for 2 seconds - Files save to platform/screenshots/ with timestamp names - Works from any page in the Atelier shell Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/lib/components/layout/AppShell.svelte | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/frontend-v2/src/lib/components/layout/AppShell.svelte b/frontend-v2/src/lib/components/layout/AppShell.svelte index b17163f..f4c44b7 100644 --- a/frontend-v2/src/lib/components/layout/AppShell.svelte +++ b/frontend-v2/src/lib/components/layout/AppShell.svelte @@ -8,7 +8,6 @@ CircleDot, Compass, Dumbbell, - FolderOpen, Landmark, LibraryBig, Menu, @@ -16,7 +15,7 @@ Search, Settings2, SquareCheckBig, - Tag, + Upload, X } from '@lucide/svelte'; @@ -52,6 +51,33 @@ ); let mobileNavOpen = $state(false); + let uploadInput: HTMLInputElement; + let uploadStatus = $state<'' | 'uploading' | 'done'>(''); + + async function handleUpload(file: File) { + uploadStatus = 'uploading'; + const fd = new FormData(); + fd.append('file', file); + try { + await fetch('/upload', { method: 'POST', body: fd, credentials: 'include' }); + uploadStatus = 'done'; + setTimeout(() => uploadStatus = '', 2000); + } catch { + uploadStatus = ''; + } + } + + function onUploadInput(e: Event) { + const input = e.target as HTMLInputElement; + if (input.files?.[0]) handleUpload(input.files[0]); + input.value = ''; + } + + function onRailDrop(e: DragEvent) { + e.preventDefault(); + const file = e.dataTransfer?.files[0]; + if (file) handleUpload(file); + } // Brain sidebar sub-items interface BrainFolder { id: string; name: string; item_count: number; } @@ -116,7 +142,13 @@ -
+ +
e.preventDefault()} ondrop={onRailDrop}> + +
{new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric' }).format(new Date())} @@ -383,6 +415,26 @@ border-top: 1px solid var(--shell-line); } + .rail-upload { + display: flex; + align-items: center; + gap: 8px; + padding: 9px 12px; + border-radius: 999px; + border: 1px dashed var(--shell-line); + background: none; + color: var(--shell-muted); + font-size: 0.78rem; + font-family: inherit; + cursor: pointer; + transition: all 160ms; + } + .rail-upload:hover { + border-color: var(--shell-ink); + color: var(--shell-ink); + background: rgba(255,255,255,0.3); + } + .rail-date { display: inline-flex; align-items: center;