feat: upload button in AppShell sidebar — click or drag to upload screenshots
- 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) <noreply@anthropic.com>
This commit is contained in:
@@ -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 @@
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="rail-bottom">
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div class="rail-bottom" ondragover={(e) => e.preventDefault()} ondrop={onRailDrop}>
|
||||
<button class="rail-upload" onclick={() => uploadInput?.click()}>
|
||||
<Upload size={14} strokeWidth={1.8} />
|
||||
<span>{uploadStatus === 'uploading' ? 'Uploading...' : uploadStatus === 'done' ? 'Saved!' : 'Upload screenshot'}</span>
|
||||
</button>
|
||||
<input bind:this={uploadInput} type="file" accept="image/*,.pdf" onchange={onUploadInput} hidden />
|
||||
<div class="rail-date">
|
||||
<CalendarDays size={14} strokeWidth={1.8} />
|
||||
<span>{new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric' }).format(new Date())}</span>
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user