/** * Agents page functionality */ import { escapeHtml, fetchData, formatRelativeTime, getQueryParam, getVSCodeInstallUrl, setupActionHandlers, setupDropdownCloseHandlers, updateQueryParams, } from '../utils'; import { openCardDetailsModal, setupModal } from '../modal'; import { renderAgentsHtml, sortAgents, type AgentSortOption, type RenderableAgent, } from './agents-render'; interface Agent extends RenderableAgent { id?: string; lastUpdated?: string | null; } interface AgentsData { items: Agent[]; } let allItems: Agent[] = []; let agentByPath = new Map(); let currentSort: AgentSortOption = 'title'; let resourceListHandlersReady = false; let modalReady = false; function applyFiltersAndRender(): void { const countEl = document.getElementById('results-count'); const results = sortAgents(allItems, currentSort); renderItems(results); if (countEl) { countEl.textContent = `${results.length} agent${results.length === 1 ? '' : 's'}`; } } function renderItems(items: Agent[]): void { const list = document.getElementById('resource-list'); if (!list) return; list.innerHTML = renderAgentsHtml(items); } function openAgentDetailsModal(path: string, trigger?: HTMLElement): void { const item = agentByPath.get(path); if (!item) { return; } const metaParts: string[] = []; if (item.model) { metaParts.push( `${escapeHtml( Array.isArray(item.model) ? item.model.join(', ') : item.model )}` ); } if (item.hasHandoffs) { metaParts.push('handoffs'); } if (item.lastUpdated) { metaParts.push( `Updated ${escapeHtml( formatRelativeTime(item.lastUpdated) )}` ); } const toolItems = item.tools || []; const displayTools = toolItems.slice(0, 24); const tagParts = displayTools.map( (tool) => `${escapeHtml(tool)}` ); if (toolItems.length > displayTools.length) { tagParts.push( `+${toolItems.length - displayTools.length} more` ); } const vscodeUrl = getVSCodeInstallUrl('agent', path, false); const insidersUrl = getVSCodeInstallUrl('agent', path, true); const actions = [ vscodeUrl ? `Install (VS Code)` : '', insidersUrl ? `Install (Insiders)` : '', ``, ].filter(Boolean); openCardDetailsModal({ title: item.title, description: item.description || 'No description', previewIcon: '🤖', previewText: 'Agent metadata and install options', metaHtml: metaParts.join(''), tagsHtml: tagParts.join(''), actionsHtml: actions.join(''), trigger, }); } function setupResourceListHandlers(list: HTMLElement | null): void { if (!list || resourceListHandlersReady) return; list.addEventListener('click', (event) => { const target = event.target as HTMLElement; if (target.closest('.resource-actions')) { return; } const item = target.closest('.resource-item') as HTMLElement | null; const button = item?.querySelector('.resource-preview') as HTMLElement | undefined; const path = item?.dataset.path; if (path) { openAgentDetailsModal(path, button); } }); resourceListHandlersReady = true; } function syncUrlState(): void { updateQueryParams({ q: '', model: [], tool: [], handoffs: false, sort: currentSort === 'title' ? '' : currentSort, }); } export async function initAgentsPage(): Promise { const list = document.getElementById('resource-list'); const sortSelect = document.getElementById('sort-select') as HTMLSelectElement; if (!modalReady) { setupModal(); modalReady = true; } setupResourceListHandlers(list as HTMLElement | null); const data = await fetchData('agents.json'); if (!data || !data.items) { if (list) list.innerHTML = '

Failed to load data

'; return; } allItems = data.items; agentByPath = new Map(allItems.map((item) => [item.path, item])); const initialSort = getQueryParam('sort'); if (initialSort === 'lastUpdated') { currentSort = initialSort; if (sortSelect) sortSelect.value = initialSort; } sortSelect?.addEventListener('change', () => { currentSort = sortSelect.value as AgentSortOption; applyFiltersAndRender(); syncUrlState(); }); applyFiltersAndRender(); setupDropdownCloseHandlers(); setupActionHandlers(); } // Auto-initialize when DOM is ready document.addEventListener('DOMContentLoaded', initAgentsPage);