const ORIGINAL_MARKETPLACE_URL = 'https://git.waifuboard.app/ItsSkaiya/WaifuBoard-Extensions/raw/branch/main/marketplace.json'; const MARKETPLACE_JSON_URL = `/api/proxy?url=${encodeURIComponent(ORIGINAL_MARKETPLACE_URL)}`; const INSTALLED_EXTENSIONS_API = '/api/extensions'; const UPDATE_EXTENSIONS_API = '/api/extensions/update'; const marketplaceContent = document.getElementById('marketplace-content'); const filterSelect = document.getElementById('extension-filter'); const updateAllBtn = document.getElementById('btn-update-all'); const modal2 = document.getElementById('customModal'); const modalTitle = document.getElementById('modalTitle'); const modalMessage = document.getElementById('modalMessage'); const modalConfirmBtn = document.getElementById('modalConfirmButton'); const modalCloseBtn = document.getElementById('modalCloseButton'); let marketplaceMetadata = {}; let installedExtensions = []; let currentTab = 'marketplace'; async function loadMarketplace() { showSkeletons(); try { const [metaRes, installedRes] = await Promise.all([ fetch(MARKETPLACE_JSON_URL).then(res => res.json()), fetch(INSTALLED_EXTENSIONS_API).then(res => res.json()) ]); marketplaceMetadata = metaRes.extensions; installedExtensions = (installedRes.extensions || []).map(e => e.toLowerCase()); initTabs(); renderGroupedView(); if (filterSelect) { filterSelect.addEventListener('change', () => renderGroupedView()); } if (updateAllBtn) { updateAllBtn.onclick = handleUpdateAll; } } catch (error) { console.error('Error loading marketplace:', error); marketplaceContent.innerHTML = `
Error al cargar el marketplace.
`; } } function initTabs() { const tabs = document.querySelectorAll('.tab-button'); tabs.forEach(tab => { tab.onclick = () => { tabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); currentTab = tab.dataset.tab; if (updateAllBtn) { if (currentTab === 'installed') { updateAllBtn.classList.remove('hidden'); } else { updateAllBtn.classList.add('hidden'); } } renderGroupedView(); }; }); } async function handleUpdateAll() { const originalText = updateAllBtn.innerText; try { updateAllBtn.disabled = true; updateAllBtn.innerText = 'Updating...'; const res = await fetch(UPDATE_EXTENSIONS_API, { method: 'POST' }); if (!res.ok) throw new Error('Update failed'); const data = await res.json(); if (data.updated && data.updated.length > 0) { const list = data.updated.join(', '); window.NotificationUtils.success(`Updated: ${list}`); await loadMarketplace(); } else { window.NotificationUtils.info('Everything is up to date.'); } } catch (error) { console.error('Update All Error:', error); window.NotificationUtils.error('Failed to perform bulk update.'); } finally { updateAllBtn.disabled = false; updateAllBtn.innerText = originalText; } } function renderGroupedView() { marketplaceContent.innerHTML = ''; const activeFilter = filterSelect.value; const groups = {}; let listToRender = []; if (currentTab === 'marketplace') { for (const [id, data] of Object.entries(marketplaceMetadata)) { listToRender.push({ id, ...data, isInstalled: installedExtensions.includes(id.toLowerCase()) }); } } else { for (const [id, data] of Object.entries(marketplaceMetadata)) { if (installedExtensions.includes(id.toLowerCase())) { listToRender.push({ id, ...data, isInstalled: true }); } } installedExtensions.forEach(id => { const existsInMeta = Object.keys(marketplaceMetadata).some(k => k.toLowerCase() === id); if (!existsInMeta) { listToRender.push({ id: id, name: id.charAt(0).toUpperCase() + id.slice(1), type: 'Local', author: 'Unknown', isInstalled: true }); } }); } listToRender.forEach(ext => { const type = ext.type || 'Other'; if (activeFilter !== 'All' && type !== activeFilter) return; if (!groups[type]) groups[type] = []; groups[type].push(ext); }); const sortedTypes = Object.keys(groups).sort(); if (sortedTypes.length === 0) { marketplaceContent.innerHTML = `

No extensions found for this criteria.

`; return; } sortedTypes.forEach(type => { const section = document.createElement('div'); section.className = 'category-group'; const title = document.createElement('h2'); title.className = 'marketplace-section-title'; title.innerText = type.replace('-', ' '); const grid = document.createElement('div'); grid.className = 'marketplace-grid'; groups[type].forEach(ext => grid.appendChild(createCard(ext))); section.appendChild(title); section.appendChild(grid); marketplaceContent.appendChild(section); }); } function createCard(ext) { const card = document.createElement('div'); card.className = `extension-card ${ext.nsfw ? 'nsfw-ext' : ''} ${ext.broken ? 'broken-ext' : ''}`; const iconUrl = `https://www.google.com/s2/favicons?domain=${ext.domain}&sz=128`; let buttonHtml = ''; if (ext.isInstalled) { buttonHtml = ``; } else if (ext.broken) { buttonHtml = ``; } else { buttonHtml = ``; } card.innerHTML = `

${ext.name}

by ${ext.author || 'Unknown'}

${ext.description || 'No description available.'}

${ext.isInstalled ? 'Installed' : (ext.broken ? 'Broken' : 'Available')} ${ext.nsfw ? 'NSFW' : ''}
${buttonHtml} `; const btn = card.querySelector('.extension-action-button'); if (!ext.broken || ext.isInstalled) { btn.onclick = () => ext.isInstalled ? promptUninstall(ext) : handleInstall(ext); } return card; } function showModal(title, message, showConfirm = false, onConfirm = null) { modalTitle.innerText = title; modalMessage.innerText = message; if (showConfirm) { modalConfirmBtn.classList.remove('hidden'); modalConfirmBtn.onclick = () => { hideModal(); if (onConfirm) onConfirm(); }; } else { modalConfirmBtn.classList.add('hidden'); } modalCloseBtn.onclick = hideModal; modal2.classList.remove('hidden'); } function hideModal() { modal2.classList.add('hidden'); } async function handleInstall(ext) { try { const res = await fetch('/api/extensions/install', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: ext.entry }) }); if (res.ok) { installedExtensions.push(ext.id.toLowerCase()); renderGroupedView(); window.NotificationUtils.success(`${ext.name} installed!`); } } catch (e) { window.NotificationUtils.error('Install failed.'); } } function promptUninstall(ext) { showModal('Confirm', `Uninstall ${ext.name}?`, true, () => handleUninstall(ext)); } async function handleUninstall(ext) { try { const res = await fetch('/api/extensions/uninstall', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fileName: ext.id + '.js' }) }); if (res.ok) { installedExtensions = installedExtensions.filter(id => id !== ext.id.toLowerCase()); renderGroupedView(); window.NotificationUtils.info(`${ext.name} uninstalled.`); } } catch (e) { window.NotificationUtils.error('Uninstall failed.'); } } function showSkeletons() { marketplaceContent.innerHTML = `
${Array(3).fill('
').join('')}
`; } document.addEventListener('DOMContentLoaded', loadMarketplace);