function app() { return { inboxFiles: [], outboxFiles: [], dragover: false, toast: { show: false, message: '' }, async loadFiles() { const [inbox, outbox] = await Promise.all([ fetch('/api/files?dir=inbox').then(r => r.json()), fetch('/api/files?dir=outbox').then(r => r.json()) ]); this.inboxFiles = inbox.files || []; this.outboxFiles = outbox.files || []; }, showToast(msg) { this.toast.message = msg; this.toast.show = true; setTimeout(() => { this.toast.show = false; }, 2500); }, async upload(event, dir) { const files = event.target.files; if (!files || !files.length) return; for (const file of files) { const form = new FormData(); form.append('file', file); form.append('dir', dir); await fetch('/api/upload', { method: 'POST', body: form }); } event.target.value = ''; this.showToast('File uploaded'); this.loadFiles(); }, async uploadDropped(event, dir) { const files = event.dataTransfer.files; if (!files || !files.length) return; for (const file of files) { const form = new FormData(); form.append('file', file); form.append('dir', dir); await fetch('/api/upload', { method: 'POST', body: form }); } this.showToast('File uploaded'); this.loadFiles(); }, download(id) { window.location.href = '/api/files/' + id + '/download'; }, async deleteFile(id) { if (!confirm('Delete this file?')) return; await fetch('/api/files/' + id, { method: 'DELETE' }); this.showToast('File deleted'); this.loadFiles(); }, async share(id) { const res = await fetch('/api/files/' + id + '/share', { method: 'POST' }); const data = await res.json(); try { await navigator.clipboard.writeText(data.url); this.showToast('Share link copied to clipboard'); } catch (_) { alert('Share URL: ' + data.url); } }, async logout() { await fetch('/api/logout', { method: 'POST' }); window.location.href = '/login'; } }; }