export function formatTimestamp(timestamp: string): string { const date = new Date(timestamp); return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', }); } export function formatDate(timestamp: string): string { const date = new Date(timestamp); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', }); } export function formatDuration(ms: number): string { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); if (hours > 0) { return `${hours}h ${minutes % 60}m`; } if (minutes > 0) { return `${minutes}m ${seconds % 60}s`; } return `${seconds}s`; } export function formatNumber(num: number, decimals = 2): string { if (Math.abs(num) >= 1e6) { return `${(num / 1e6).toFixed(decimals)}M`; } if (Math.abs(num) >= 1e3) { return `${(num / 1e3).toFixed(decimals)}K`; } return num.toFixed(decimals); } export function formatReward(reward: number): string { const sign = reward >= 0 ? '+' : ''; return `${sign}${reward.toFixed(3)}`; } export function truncateText(text: string, maxLength: number): string { if (text.length <= maxLength) return text; return `${text.slice(0, maxLength - 3)}...`; } export function classNames(...classes: (string | boolean | undefined | null)[]): string { return classes.filter(Boolean).join(' '); } export function getStatusColor(status: string): string { const colors: Record = { idle: 'yellow', thinking: 'blue', acting: 'green', waiting: 'orange', error: 'red', running: 'green', completed: 'green', failed: 'red', pending: 'gray', timeout: 'orange', }; return colors[status] ?? 'gray'; } export function getRoleIcon(role: string): string { const icons: Record = { navigator: '🧭', extractor: '📊', validator: '✅', coordinator: '🎯', }; return icons[role] ?? '🤖'; } export function getActionIcon(actionType: string): string { const icons: Record = { navigate: '🔗', click: '👆', extract: '📤', scroll: '📜', input: '⌨️', wait: '⏳', screenshot: '📸', execute_tool: '🔧', delegate: '👥', terminate: '🛑', }; return icons[actionType] ?? '⚡'; } export function debounce unknown>( func: T, wait: number ): (...args: Parameters) => void { let timeout: ReturnType | null = null; return (...args: Parameters) => { if (timeout) clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } export function throttle unknown>( func: T, limit: number ): (...args: Parameters) => void { let inThrottle = false; return (...args: Parameters) => { if (!inThrottle) { func(...args); inThrottle = true; setTimeout(() => (inThrottle = false), limit); } }; } export function generateId(): string { return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`; } export function parseJSON(json: string, fallback: T): T { try { return JSON.parse(json) as T; } catch { return fallback; } } export function calculateProgress(current: number, total: number): number { if (total === 0) return 0; return Math.min(Math.round((current / total) * 100), 100); } export function groupBy(array: T[], key: keyof T): Record { return array.reduce((groups, item) => { const groupKey = String(item[key]); return { ...groups, [groupKey]: [...(groups[groupKey] || []), item], }; }, {} as Record); } export function sortByTimestamp( items: T[], order: 'asc' | 'desc' = 'desc' ): T[] { return [...items].sort((a, b) => { const timeA = new Date(a.timestamp).getTime(); const timeB = new Date(b.timestamp).getTime(); return order === 'asc' ? timeA - timeB : timeB - timeA; }); }