// Simple client-side router with proper state management const router = { currentRoute: '/', refreshTimeout: null, detailRefreshTimeout: null, currentInstanceId: null, initialized: false, renderInProgress: false, init() { console.log('[Router] init() called'); if (this.initialized) { console.log('[Router] Already initialized, skipping'); return; } this.initialized = true; // Handle browser back/forward window.addEventListener('popstate', () => { console.log('[Router] popstate event'); this.handleRoute(window.location.pathname); }); // Handle initial route - call once after a short delay to ensure DOM is ready setTimeout(() => { console.log('[Router] Initial route handling'); this.handleRoute(window.location.pathname); }, 100); }, navigate(path) { console.log('[Router] navigate:', path); // Cancel any pending refreshes this.cancelRefreshes(); window.history.pushState({}, '', path); this.handleRoute(path); }, cancelRefreshes() { if (this.refreshTimeout) { console.log('[Router] Cancelling home refresh timeout'); clearTimeout(this.refreshTimeout); this.refreshTimeout = null; } if (this.detailRefreshTimeout) { console.log('[Router] Cancelling detail refresh timeout'); clearTimeout(this.detailRefreshTimeout); this.detailRefreshTimeout = null; } }, async handleRoute(path) { this.currentRoute = path; console.log('[Router] handleRoute:', path); const container = document.getElementById('page-container'); if (!container) { console.error('[Router] page-container not found!'); return; } // Cancel any pending refreshes when route changes this.cancelRefreshes(); if (path === '/' || path === '') { await this.renderHome(container); } else if (path.startsWith('/instance/')) { const id = path.split('/')[2]; await this.renderDetail(container, id); } else { container.innerHTML = components.error('Page not found'); } }, async renderHome(container) { console.log('[Router] renderHome called, renderInProgress:', this.renderInProgress); // Prevent concurrent renders if (this.renderInProgress) { console.log('[Router] Render already in progress, skipping'); return; } this.renderInProgress = true; try { console.log('[Router] Showing spinner'); container.innerHTML = components.spinner('Loading instances...'); console.log('[Router] Fetching instances from API'); const instances = await api.getInstances(); console.log('[Router] Received', instances.length, 'instances'); // Check if we're still on the home route (user might have navigated away) if (this.currentRoute !== '/' && this.currentRoute !== '') { console.log('[Router] Route changed during fetch, aborting render'); return; } if (instances.length === 0) { console.log('[Router] No instances, showing empty state'); container.innerHTML = components.emptyState( 'No running instances. Click "+ New Run" to start one.' ); } else { console.log('[Router] Building HTML for', instances.length, 'instances'); let html = '
No tool calls yet
'; } html += `Loading...
${components.escapeHtml(data.content)}`;
// Apply syntax highlighting
content.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
} catch (error) {
content.innerHTML = ``;
}
};
// Close full file modal
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('full-file-close')?.addEventListener('click', () => {
document.getElementById('full-file-modal').classList.add('hidden');
});
});
// Expose to window for global access
window.router = router;