// static/js/addons.js - Addon manager for manifest-based Odysseus addons import uiModule from './ui.js'; let initialized = false; let currentManifest = null; function el(id) { return document.getElementById(id); } function esc(value) { return uiModule && typeof uiModule.esc === 'function' ? uiModule.esc(String(value ?? '')) : String(value ?? '').replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); } async function api(path, options = {}) { const res = await fetch(path, { credentials: 'same-origin', headers: { 'Content-Type': 'application/json', ...(options.headers || {}) }, ...options, }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data.detail || data.error || `HTTP ${res.status}`); return data; } function contributionSummary(addon) { const items = Array.isArray(addon.contributions) ? addon.contributions : []; if (!items.length) return 'No contributions'; const counts = new Map(); items.forEach(item => counts.set(item.type || 'unknown', (counts.get(item.type || 'unknown') || 0) + 1)); return Array.from(counts.entries()) .map(([type, count]) => `${esc(type)}${count > 1 ? ` x${count}` : ''}`) .join(''); } function renderAddons(addons) { const list = el('addons-list'); if (!list) return; if (!addons.length) { list.innerHTML = '