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 = `
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);