function mediaCopyRef(ref) { var text = '![](' + ref + ')'; navigator.clipboard.writeText(text).then(function() { showToast('Copied: ' + text); }); } function mediaFilterFolder(folder, btn) { var cards = document.querySelectorAll('.media-card'); cards.forEach(function(card) { if (folder === null) { card.classList.remove('hidden'); } else { card.classList.toggle('hidden', card.dataset.folder !== folder); } }); // Update selected button var buttons = btn.parentElement.querySelectorAll('.btn-small'); buttons.forEach(function(b) { b.classList.remove('is-selected'); }); btn.classList.add('is-selected'); } function mediaDeleteFile(id, name) { if (!confirm('Delete "' + name + '"? This cannot be undone. Existing references in your content will break.')) return; fetch('/api/media/' + id, { method: 'DELETE', credentials: 'same-origin', headers: csrfHeaders() }).then(function(res) { if (res.ok) { document.getElementById('tab-media').click(); } else { res.text().then(function(t) { showToast(t || 'Failed to delete file.'); }); } }).catch(function() { showToast('Network error. Please check your connection and try again.'); }); } function mediaHandleDrop(e) { e.preventDefault(); if (e.dataTransfer && e.dataTransfer.files.length > 0) { mediaUploadFiles(e.dataTransfer.files); } } function mediaUploadFiles(fileList) { var folder = (document.getElementById('media-folder-input').value || '').trim(); var progress = document.getElementById('media-upload-progress'); progress.innerHTML = ''; for (var i = 0; i < fileList.length; i++) { (function(file) { var statusEl = document.createElement('div'); statusEl.className = 'user-media-upload-status'; statusEl.textContent = 'Uploading ' + file.name + '...'; progress.appendChild(statusEl); // 1. Presign fetch('/api/media/presign', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', ...csrfHeaders() }, body: JSON.stringify({ file_name: file.name, content_type: file.type, folder: folder }) }).then(function(res) { if (!res.ok) return res.text().then(function(t) { throw new Error(t); }); return res.json(); }).then(function(data) { // 2. Upload to S3 var headers = { 'Content-Type': file.type }; if (data.cache_control) headers['Cache-Control'] = data.cache_control; return fetch(data.upload_url, { method: 'PUT', headers: headers, body: file }).then(function(putRes) { if (!putRes.ok) throw new Error('S3 upload failed'); return data; }); }).then(function(data) { // 3. Confirm return fetch('/api/media/confirm', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', ...csrfHeaders() }, body: JSON.stringify({ s3_key: data.s3_key, file_name: file.name, content_type: file.type, folder: folder }) }).then(function(res) { if (!res.ok) return res.text().then(function(t) { throw new Error(t); }); statusEl.textContent = file.name + ' uploaded.'; statusEl.classList.add('user-media-upload-status--ok'); }); }).catch(function(err) { statusEl.textContent = file.name + ': ' + (err.message || 'Upload failed'); statusEl.classList.add('user-media-upload-status--err'); }).finally(function() { // Refresh tab after last file var remaining = progress.querySelectorAll('.user-media-upload-status:not(.user-media-upload-status--ok):not(.user-media-upload-status--err)'); if (remaining.length === 0) { setTimeout(function() { document.getElementById('tab-media').click(); }, 1000); } }); })(fileList[i]); } }