/* Atlus — Package Manager app */ (function () { 'use strict'; let container = null; let searchInput = null; let listEl = null; let statusEl = null; let searchTimeout = null; let expandedPkg = null; // currently expanded package name // ---- Search ---- async function searchPackages(query) { if (!query || query.length < 2) { listEl.innerHTML = '
Type at least 2 characters to search packages
'; return; } listEl.innerHTML = '
Searching…
'; try { const res = await Atlus.apiFetch(`/api/packages/search?q=${encodeURIComponent(query)}`); if (res.status === 503) { listEl.innerHTML = '
Package manager not available on this system
'; return; } if (!res.ok) { const err = await res.json().catch(() => ({ detail: `HTTP ${res.status}` })); throw new Error(err.detail || `Search failed (${res.status})`); } const results = await res.json(); if (results.length === 0) { listEl.innerHTML = '
No packages found
'; return; } renderResults(results); updateStatus(`${results.length} result${results.length !== 1 ? 's' : ''}`); } catch (e) { listEl.innerHTML = `
Error: ${e.message}
`; } } // ---- Render results ---- function renderResults(results) { listEl.innerHTML = ''; results.forEach(pkg => { const row = document.createElement('div'); row.className = 'pkg-row'; row.dataset.name = pkg.name; row.innerHTML = `
${pkg.name} ${pkg.summary || ''}
`; const header = row.querySelector('.pkg-row-header'); const detail = row.querySelector('.pkg-detail'); header.addEventListener('click', () => { if (expandedPkg === pkg.name) { // Collapse detail.classList.add('hidden'); row.classList.remove('expanded'); row.querySelector('.pkg-expand-icon').textContent = '▸'; expandedPkg = null; } else { // Collapse previous if (expandedPkg) { const prev = listEl.querySelector(`.pkg-row[data-name="${expandedPkg}"]`); if (prev) { prev.querySelector('.pkg-detail').classList.add('hidden'); prev.classList.remove('expanded'); prev.querySelector('.pkg-expand-icon').textContent = '▸'; } } // Expand this expandedPkg = pkg.name; row.classList.add('expanded'); row.querySelector('.pkg-expand-icon').textContent = '▾'; loadPackageDetail(pkg.name, detail); } }); listEl.appendChild(row); }); } // ---- Package detail ---- async function loadPackageDetail(name, detailEl) { detailEl.classList.remove('hidden'); detailEl.innerHTML = '
Loading…
'; try { const res = await Atlus.apiFetch(`/api/packages/info/${encodeURIComponent(name)}`); if (!res.ok) throw new Error('Failed to load package info'); const info = await res.json(); renderDetail(info, detailEl); } catch (e) { detailEl.innerHTML = `
Error: ${e.message}
`; } } function renderDetail(info, detailEl) { const installedBadge = info.installed ? `Installed ${info.installed_version || ''}` : `Not installed`; const sizeText = info.installed_size ? `${info.installed_size} KB installed` : (info.size ? `${Atlus.formatBytes(parseInt(info.size))} download` : ''); detailEl.innerHTML = `
v${info.version} ${installedBadge}
${info.installed ? `` : `` }
${info.description || 'No description available'}
${info.section ? `Section: ${info.section}` : ''} ${sizeText ? `Size: ${sizeText}` : ''} ${info.architecture ? `Arch: ${info.architecture}` : ''}
${info.depends ? `
Depends: ${info.depends}
` : ''} ${info.homepage ? `
Homepage: ${info.homepage}
` : ''} `; // Action button handler const actionBtn = detailEl.querySelector('.pkg-action-btn'); if (actionBtn) { actionBtn.addEventListener('click', (e) => { e.stopPropagation(); const pkgName = actionBtn.dataset.name; if (actionBtn.classList.contains('install')) { installPackage(pkgName, actionBtn, detailEl); } else { removePackage(pkgName, actionBtn, detailEl); } }); } } // ---- Install / Remove ---- async function installPackage(name, btn, detailEl) { btn.disabled = true; btn.textContent = 'Installing…'; updateStatus(`Installing ${name}…`); try { const res = await Atlus.apiFetch('/api/packages/install', { method: 'POST', body: { name }, }); if (!res.ok) { const err = await res.json(); throw new Error(err.detail || 'Install failed'); } updateStatus(`${name} installed successfully`); // Reload detail to reflect new status await loadPackageDetail(name, detailEl); } catch (e) { btn.disabled = false; btn.textContent = 'Install'; updateStatus(`Error: ${e.message}`); } } async function removePackage(name, btn, detailEl) { if (!confirm(`Remove package "${name}"?`)) return; btn.disabled = true; btn.textContent = 'Removing…'; updateStatus(`Removing ${name}…`); try { const res = await Atlus.apiFetch('/api/packages/remove', { method: 'POST', body: { name }, }); if (!res.ok) { const err = await res.json(); throw new Error(err.detail || 'Remove failed'); } updateStatus(`${name} removed successfully`); // Reload detail to reflect new status await loadPackageDetail(name, detailEl); } catch (e) { btn.disabled = false; btn.textContent = 'Remove'; updateStatus(`Error: ${e.message}`); } } // ---- Refresh cache ---- async function refreshCache(btn) { btn.disabled = true; btn.textContent = 'Updating…'; updateStatus('Updating package cache…'); try { const res = await Atlus.apiFetch('/api/packages/update-cache', { method: 'POST', }); if (!res.ok) throw new Error('Cache update failed'); updateStatus('Package cache updated'); } catch (e) { updateStatus(`Error: ${e.message}`); } btn.disabled = false; btn.textContent = 'Refresh Cache'; } // ---- Status ---- function updateStatus(text) { if (statusEl) statusEl.textContent = text; } // ---- App registration ---- Atlus.registerApp('packages', { title: 'Packages', init(el) { container = el; container.classList.add('app-packages'); // Toolbar const toolbar = document.createElement('div'); toolbar.className = 'pkg-toolbar'; searchInput = document.createElement('input'); searchInput.type = 'text'; searchInput.className = 'pkg-search'; searchInput.placeholder = 'Search packages…'; searchInput.addEventListener('input', () => { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { searchPackages(searchInput.value.trim()); }, 300); }); toolbar.appendChild(searchInput); const cacheBtn = document.createElement('button'); cacheBtn.className = 'pkg-cache-btn'; cacheBtn.textContent = 'Refresh Cache'; cacheBtn.addEventListener('click', () => refreshCache(cacheBtn)); toolbar.appendChild(cacheBtn); container.appendChild(toolbar); // Results list listEl = document.createElement('div'); listEl.className = 'pkg-list'; listEl.innerHTML = '
Type at least 2 characters to search packages
'; container.appendChild(listEl); // Status bar statusEl = document.createElement('div'); statusEl.className = 'pkg-status'; statusEl.textContent = 'Ready'; container.appendChild(statusEl); // Focus search setTimeout(() => searchInput.focus(), 100); }, destroy() { clearTimeout(searchTimeout); container = null; searchInput = null; listEl = null; statusEl = null; expandedPkg = null; }, }); })();