class ArchAppDrawer extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.apps = []; } connectedCallback() { this.render(); this.loadApps(); this.setupSearch(); this.setupCloseGesture(); } render() { this.shadowRoot.innerHTML = `
$ pacman -Ss [query]
swipe down to close
`; } loadApps() { // Get apps from main script if (window.launcher) { this.apps = window.launcher.getAllApps(); this.renderAppList(this.apps); } else { // Fallback setTimeout(() => this.loadApps(), 100); } } renderAppList(apps) { const list = this.shadowRoot.getElementById('app-list'); if (!list) return; // Group by category const grouped = apps.reduce((acc, app) => { if (!acc[app.category]) acc[app.category] = []; acc[app.category].push(app); return acc; }, {}); list.innerHTML = ''; Object.entries(grouped).forEach(([category, categoryApps]) => { const catEl = document.createElement('div'); catEl.className = 'category'; catEl.textContent = category; list.appendChild(catEl); categoryApps.forEach(app => { const item = document.createElement('div'); item.className = 'app-item'; item.innerHTML = `
${app.name}
:${app.category}/${app.name.toLowerCase()}
`; item.addEventListener('click', () => { this.dispatchEvent(new CustomEvent('launch-app', { bubbles: true, composed: true, detail: app })); }); list.appendChild(item); }); }); if (window.feather) { feather.replace({ parent: this.shadowRoot }); } } setupSearch() { const input = this.shadowRoot.getElementById('search-input'); if (!input) return; input.addEventListener('input', (e) => { const query = e.target.value.toLowerCase(); if (!query) { this.renderAppList(this.apps); return; } const filtered = this.apps.filter(app => app.name.toLowerCase().includes(query) || app.category.toLowerCase().includes(query) ); const list = this.shadowRoot.getElementById('app-list'); if (filtered.length === 0) { list.innerHTML = '
no packages found matching "' + query + '"
'; } else { this.renderAppList(filtered); } }); } setupCloseGesture() { let startY = 0; const drawer = this; drawer.addEventListener('touchstart', (e) => { startY = e.touches[0].clientY; }, { passive: true }); drawer.addEventListener('touchmove', (e) => { const currentY = e.touches[0].clientY; const diff = currentY - startY; if (diff > 0 && drawer.scrollTop === 0) { // User is pulling down at top of scroll } }, { passive: true }); } } customElements.define('arch-app-drawer', ArchAppDrawer);