// Asset library rendering. Calls API.listAssets, renders a grid into // #asset-grid, tracks the selected asset for the action buttons. (function () { const Library = {}; Library.state = { assets: [], selectedId: null }; Library.render = function () { const grid = UI.$('#asset-grid'); grid.innerHTML = ''; if (!Library.state.assets.length) { const e = document.createElement('div'); e.className = 'empty muted'; e.textContent = 'No assets'; grid.appendChild(e); return; } for (const a of Library.state.assets) { grid.appendChild(makeCard(a)); } Library.syncActions(); }; function makeCard(asset) { const card = document.createElement('div'); card.className = 'asset-card'; if (asset.id === Library.state.selectedId) card.classList.add('selected'); card.dataset.assetId = asset.id; const thumbKey = asset.thumbnail_s3_key || asset.thumbnail || null; const thumbUrl = thumbKey ? `${API.state.serverUrl}/api/v1/assets/${asset.id}/thumbnail?redirect=1` : null; if (thumbUrl) { const img = document.createElement('img'); img.className = 'asset-thumb'; img.alt = asset.display_name || asset.filename || asset.id; // UXP fetch supports cross-origin images; the redirect=1 query on // /thumbnail tells the server to 302 to the presigned S3 URL. img.src = thumbUrl; img.onerror = () => { img.replaceWith(placeholder()); }; card.appendChild(img); } else { card.appendChild(placeholder()); } const name = document.createElement('div'); name.className = 'asset-name'; name.textContent = asset.display_name || asset.filename || asset.id; card.appendChild(name); card.addEventListener('click', () => Library.select(asset.id)); return card; } function placeholder() { const p = document.createElement('div'); p.className = 'asset-thumb-placeholder'; p.textContent = 'no preview'; return p; } Library.select = function (id) { Library.state.selectedId = id; Library.render(); }; Library.selectedAsset = function () { return Library.state.assets.find(a => a.id === Library.state.selectedId) || null; }; Library.syncActions = function () { const sel = Library.selectedAsset(); const info = UI.$('#selected-info'); if (sel) { info.textContent = (sel.display_name || sel.filename || sel.id) + (sel.file_size ? ' · ' + UI.formatBytes(Number(sel.file_size)) : ''); info.classList.remove('muted'); } else { info.textContent = 'No asset selected'; info.classList.add('muted'); } UI.$('#import-proxy-btn').disabled = !sel; UI.$('#import-hires-btn').disabled = !sel || !sel.original_s3_key; }; Library.refresh = async function (query) { const grid = UI.$('#asset-grid'); grid.innerHTML = '
Loading…
'; try { const data = await API.listAssets(query); Library.state.assets = (data && (data.assets || data.rows)) || []; Library.render(); // Surface count via toast so we can tell empty-grid (zero) from // CSS-broken-grid (cards exist but invisible) at a glance. if (UI.toast) UI.toast('Loaded ' + Library.state.assets.length + ' assets (total ' + (data.total || '?') + ')', 'ok'); } catch (e) { grid.innerHTML = ''; const err = document.createElement('div'); err.className = 'empty muted'; err.textContent = 'Error loading assets: ' + e.message; grid.appendChild(err); if (UI.toast) UI.toast('Asset load failed: ' + e.message, 'error'); } }; window.Library = Library; })();