// @ts-nocheck — transitional: downstream files updated in Slice 6 (item-* rewrites) / Slice 4 (vitest setup) / Slice 5 (content + setup rewires) /// Entry list view — search bar, group tabs, scrollable entry list with keyboard nav. import { getState, setState, sendMessage, navigate, escapeHtml } from '../popup'; import type { ManifestEntry } from '../../shared/types'; /// Extract the domain from a URL for display. function domainOf(url: string | undefined): string { if (!url) return ''; try { return new URL(url).hostname; } catch { return ''; } } /// Derive unique group names from the current entries. function getGroups(entries: Array<[string, ManifestEntry]>): string[] { const groups = new Set(); for (const [, e] of entries) { if (e.group) groups.add(e.group); } return Array.from(groups).sort(); } export function renderEntryList(app: HTMLElement): void { const state = getState(); const groups = getGroups(state.entries); const filtered = getFilteredEntries(); const groupTabsHtml = groups.length > 0 ? `
${groups.map(g => `` ).join('')}
` : ''; const entriesHtml = filtered.length > 0 ? filtered.map(([id, e], i) => `
${escapeHtml(e.name)}
`).join('') : '
no entries
'; app.innerHTML = ` ${groupTabsHtml}
${entriesHtml}
/ search + add ↑↓ nav Enter open
`; // --- Event listeners --- const searchInput = document.getElementById('search-input') as HTMLInputElement; searchInput?.addEventListener('input', () => { setState({ searchQuery: searchInput.value, selectedIndex: 0 }); }); // Group tab clicks. const groupTabs = app.querySelectorAll('.group-tab'); groupTabs.forEach(tab => { tab.addEventListener('click', () => { const group = (tab as HTMLElement).dataset.group || null; setState({ activeGroup: group, selectedIndex: 0 }); }); }); // Entry row clicks. const rows = app.querySelectorAll('.entry-row'); rows.forEach(row => { row.addEventListener('click', async () => { const id = (row as HTMLElement).dataset.id!; await openEntry(id); }); }); // Keyboard navigation. document.addEventListener('keydown', handleListKeydown); // Focus search on / key (unless already focused). searchInput?.focus(); } async function openEntry(id: string): Promise { setState({ loading: true }); const resp = await sendMessage({ type: 'get_entry', id }); if (resp.ok) { const data = resp.data as { entry: import('../../shared/types').Entry }; navigate('detail', { selectedId: id, selectedEntry: data.entry, }); } else { setState({ loading: false, error: resp.error }); } } /// Compute the visible (filtered) entry list from current state. function getFilteredEntries(): Array<[string, ManifestEntry]> { const state = getState(); let filtered = state.entries; if (state.activeGroup) { const g = state.activeGroup.toLowerCase(); filtered = filtered.filter(([, e]) => e.group?.toLowerCase() === g); } if (state.searchQuery) { const q = state.searchQuery.toLowerCase(); filtered = filtered.filter(([, e]) => { if (e.name.toLowerCase().includes(q)) return true; if (e.url?.toLowerCase().includes(q)) return true; if (e.username?.toLowerCase().includes(q)) return true; return false; }); } filtered.sort((a, b) => a[1].name.localeCompare(b[1].name)); return filtered; } function handleListKeydown(e: KeyboardEvent): void { const state = getState(); const target = e.target as HTMLElement; const isSearch = target.id === 'search-input'; if (e.key === '/' && !isSearch) { e.preventDefault(); (document.getElementById('search-input') as HTMLInputElement)?.focus(); return; } if (e.key === '+' && !isSearch) { e.preventDefault(); navigate('add'); return; } const filtered = getFilteredEntries(); if (e.key === 'ArrowDown') { e.preventDefault(); const max = Math.max(filtered.length - 1, 0); setState({ selectedIndex: Math.min(state.selectedIndex + 1, max) }); return; } if (e.key === 'ArrowUp') { e.preventDefault(); setState({ selectedIndex: Math.max(state.selectedIndex - 1, 0) }); return; } if (e.key === 'Enter' && !isSearch) { e.preventDefault(); if (filtered[state.selectedIndex]) { openEntry(filtered[state.selectedIndex][0]); } return; } if (e.key === 'Escape') { document.removeEventListener('keydown', handleListKeydown); window.close(); return; } }