import React, { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { classNames } from '~/utils/classNames'; import { logStore } from '~/lib/stores/logs'; interface ProviderStatus { id: string; name: string; status: 'online' | 'offline' | 'error'; error?: string; } interface SystemInfo { os: string; arch: string; platform: string; cpus: string; memory: { total: string; free: string; used: string; percentage: number; }; node: string; browser: { name: string; version: string; language: string; userAgent: string; cookiesEnabled: boolean; online: boolean; platform: string; cores: number; }; screen: { width: number; height: number; colorDepth: number; pixelRatio: number; }; time: { timezone: string; offset: number; locale: string; }; performance: { memory: { jsHeapSizeLimit: number; totalJSHeapSize: number; usedJSHeapSize: number; usagePercentage: number; }; timing: { loadTime: number; domReadyTime: number; readyStart: number; redirectTime: number; appcacheTime: number; unloadEventTime: number; lookupDomainTime: number; connectTime: number; requestTime: number; initDomTreeTime: number; loadEventTime: number; }; navigation: { type: number; redirectCount: number; }; }; network: { downlink: number; effectiveType: string; rtt: number; saveData: boolean; type: string; }; battery?: { charging: boolean; chargingTime: number; dischargingTime: number; level: number; }; storage: { quota: number; usage: number; persistent: boolean; temporary: boolean; }; } export default function DebugTab() { const [providerStatuses, setProviderStatuses] = useState([]); const [systemInfo, setSystemInfo] = useState(null); const [loading, setLoading] = useState({ systemInfo: false, providers: false, performance: false, errors: false, }); const [errorLog, setErrorLog] = useState<{ errors: any[]; lastCheck: string | null; }>({ errors: [], lastCheck: null, }); // Fetch initial data useEffect(() => { checkProviderStatus(); getSystemInfo(); }, []); // Set up error listeners when component mounts useEffect(() => { const errors: any[] = []; const handleError = (event: ErrorEvent) => { errors.push({ type: 'error', message: event.message, filename: event.filename, lineNumber: event.lineno, columnNumber: event.colno, error: event.error, timestamp: new Date().toISOString(), }); }; const handleRejection = (event: PromiseRejectionEvent) => { errors.push({ type: 'unhandledRejection', reason: event.reason, timestamp: new Date().toISOString(), }); }; window.addEventListener('error', handleError); window.addEventListener('unhandledrejection', handleRejection); return () => { window.removeEventListener('error', handleError); window.removeEventListener('unhandledrejection', handleRejection); }; }, []); const checkProviderStatus = async () => { try { setLoading((prev) => ({ ...prev, providers: true })); // Fetch real provider statuses const providers: ProviderStatus[] = []; // Check OpenAI status try { const openaiResponse = await fetch('/api/providers/openai/status'); providers.push({ id: 'openai', name: 'OpenAI', status: openaiResponse.ok ? 'online' : 'error', error: !openaiResponse.ok ? 'API Error' : undefined, }); } catch { providers.push({ id: 'openai', name: 'OpenAI', status: 'offline' }); } // Check Anthropic status try { const anthropicResponse = await fetch('/api/providers/anthropic/status'); providers.push({ id: 'anthropic', name: 'Anthropic', status: anthropicResponse.ok ? 'online' : 'error', error: !anthropicResponse.ok ? 'API Error' : undefined, }); } catch { providers.push({ id: 'anthropic', name: 'Anthropic', status: 'offline' }); } // Check Local Models status try { const localResponse = await fetch('/api/providers/local/status'); providers.push({ id: 'local', name: 'Local Models', status: localResponse.ok ? 'online' : 'error', error: !localResponse.ok ? 'API Error' : undefined, }); } catch { providers.push({ id: 'local', name: 'Local Models', status: 'offline' }); } // Check Ollama status try { const ollamaResponse = await fetch('/api/providers/ollama/status'); providers.push({ id: 'ollama', name: 'Ollama', status: ollamaResponse.ok ? 'online' : 'error', error: !ollamaResponse.ok ? 'API Error' : undefined, }); } catch { providers.push({ id: 'ollama', name: 'Ollama', status: 'offline' }); } setProviderStatuses(providers); toast.success('Provider status updated'); } catch (error) { toast.error('Failed to check provider status'); console.error('Failed to check provider status:', error); } finally { setLoading((prev) => ({ ...prev, providers: false })); } }; const getSystemInfo = async () => { try { setLoading((prev) => ({ ...prev, systemInfo: true })); // Get browser info const ua = navigator.userAgent; const browserName = ua.includes('Firefox') ? 'Firefox' : ua.includes('Chrome') ? 'Chrome' : ua.includes('Safari') ? 'Safari' : ua.includes('Edge') ? 'Edge' : 'Unknown'; const browserVersion = ua.match(/(Firefox|Chrome|Safari|Edge)\/([0-9.]+)/)?.[2] || 'Unknown'; // Get performance metrics const memory = (performance as any).memory || {}; const timing = performance.timing; const navigation = performance.navigation; const connection = (navigator as any).connection; // Get battery info let batteryInfo; try { const battery = await (navigator as any).getBattery(); batteryInfo = { charging: battery.charging, chargingTime: battery.chargingTime, dischargingTime: battery.dischargingTime, level: battery.level * 100, }; } catch { console.log('Battery API not supported'); } // Get storage info let storageInfo = { quota: 0, usage: 0, persistent: false, temporary: false, }; try { const storage = await navigator.storage.estimate(); const persistent = await navigator.storage.persist(); storageInfo = { quota: storage.quota || 0, usage: storage.usage || 0, persistent, temporary: !persistent, }; } catch { console.log('Storage API not supported'); } // Get memory info from browser performance API const performanceMemory = (performance as any).memory || {}; const totalMemory = performanceMemory.jsHeapSizeLimit || 0; const usedMemory = performanceMemory.usedJSHeapSize || 0; const freeMemory = totalMemory - usedMemory; const memoryPercentage = totalMemory ? (usedMemory / totalMemory) * 100 : 0; const systemInfo: SystemInfo = { os: navigator.platform, arch: navigator.userAgent.includes('x64') ? 'x64' : navigator.userAgent.includes('arm') ? 'arm' : 'unknown', platform: navigator.platform, cpus: navigator.hardwareConcurrency + ' cores', memory: { total: formatBytes(totalMemory), free: formatBytes(freeMemory), used: formatBytes(usedMemory), percentage: Math.round(memoryPercentage), }, node: 'browser', browser: { name: browserName, version: browserVersion, language: navigator.language, userAgent: navigator.userAgent, cookiesEnabled: navigator.cookieEnabled, online: navigator.onLine, platform: navigator.platform, cores: navigator.hardwareConcurrency, }, screen: { width: window.screen.width, height: window.screen.height, colorDepth: window.screen.colorDepth, pixelRatio: window.devicePixelRatio, }, time: { timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, offset: new Date().getTimezoneOffset(), locale: navigator.language, }, performance: { memory: { jsHeapSizeLimit: memory.jsHeapSizeLimit || 0, totalJSHeapSize: memory.totalJSHeapSize || 0, usedJSHeapSize: memory.usedJSHeapSize || 0, usagePercentage: memory.totalJSHeapSize ? (memory.usedJSHeapSize / memory.totalJSHeapSize) * 100 : 0, }, timing: { loadTime: timing.loadEventEnd - timing.navigationStart, domReadyTime: timing.domContentLoadedEventEnd - timing.navigationStart, readyStart: timing.fetchStart - timing.navigationStart, redirectTime: timing.redirectEnd - timing.redirectStart, appcacheTime: timing.domainLookupStart - timing.fetchStart, unloadEventTime: timing.unloadEventEnd - timing.unloadEventStart, lookupDomainTime: timing.domainLookupEnd - timing.domainLookupStart, connectTime: timing.connectEnd - timing.connectStart, requestTime: timing.responseEnd - timing.requestStart, initDomTreeTime: timing.domInteractive - timing.responseEnd, loadEventTime: timing.loadEventEnd - timing.loadEventStart, }, navigation: { type: navigation.type, redirectCount: navigation.redirectCount, }, }, network: { downlink: connection?.downlink || 0, effectiveType: connection?.effectiveType || 'unknown', rtt: connection?.rtt || 0, saveData: connection?.saveData || false, type: connection?.type || 'unknown', }, battery: batteryInfo, storage: storageInfo, }; setSystemInfo(systemInfo); toast.success('System information updated'); } catch (error) { toast.error('Failed to get system information'); console.error('Failed to get system information:', error); } finally { setLoading((prev) => ({ ...prev, systemInfo: false })); } }; // Helper function to format bytes to human readable format const formatBytes = (bytes: number) => { const units = ['B', 'KB', 'MB', 'GB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${Math.round(size)} ${units[unitIndex]}`; }; const handleLogSystemInfo = () => { if (!systemInfo) { return; } logStore.logSystem('System Information', { os: systemInfo.os, arch: systemInfo.arch, cpus: systemInfo.cpus, memory: systemInfo.memory, node: systemInfo.node, }); toast.success('System information logged'); }; const handleLogPerformance = () => { try { setLoading((prev) => ({ ...prev, performance: true })); // Get performance metrics using modern Performance API const performanceEntries = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming; const memory = (performance as any).memory; // Calculate timing metrics const timingMetrics = { loadTime: performanceEntries.loadEventEnd - performanceEntries.startTime, domReadyTime: performanceEntries.domContentLoadedEventEnd - performanceEntries.startTime, fetchTime: performanceEntries.responseEnd - performanceEntries.fetchStart, redirectTime: performanceEntries.redirectEnd - performanceEntries.redirectStart, dnsTime: performanceEntries.domainLookupEnd - performanceEntries.domainLookupStart, tcpTime: performanceEntries.connectEnd - performanceEntries.connectStart, ttfb: performanceEntries.responseStart - performanceEntries.requestStart, processingTime: performanceEntries.loadEventEnd - performanceEntries.responseEnd, }; // Get resource timing data const resourceEntries = performance.getEntriesByType('resource'); const resourceStats = { totalResources: resourceEntries.length, totalSize: resourceEntries.reduce((total, entry) => total + (entry as any).transferSize || 0, 0), totalTime: Math.max(...resourceEntries.map((entry) => entry.duration)), }; // Get memory metrics const memoryMetrics = memory ? { jsHeapSizeLimit: memory.jsHeapSizeLimit, totalJSHeapSize: memory.totalJSHeapSize, usedJSHeapSize: memory.usedJSHeapSize, heapUtilization: (memory.usedJSHeapSize / memory.totalJSHeapSize) * 100, } : null; // Get frame rate metrics let fps = 0; if ('requestAnimationFrame' in window) { const times: number[] = []; function calculateFPS(now: number) { times.push(now); if (times.length > 10) { const fps = Math.round((1000 * 10) / (now - times[0])); times.shift(); return fps; } requestAnimationFrame(calculateFPS); return 0; } fps = calculateFPS(performance.now()); } // Log all performance metrics logStore.logSystem('Performance Metrics', { timing: timingMetrics, resources: resourceStats, memory: memoryMetrics, fps, timestamp: new Date().toISOString(), navigationEntry: { type: performanceEntries.type, redirectCount: performanceEntries.redirectCount, }, }); toast.success('Performance metrics logged'); } catch (error) { toast.error('Failed to log performance metrics'); console.error('Failed to log performance metrics:', error); } finally { setLoading((prev) => ({ ...prev, performance: false })); } }; const handleCheckErrors = () => { try { setLoading((prev) => ({ ...prev, errors: true })); // Get any errors from the performance entries const resourceErrors = performance .getEntriesByType('resource') .filter((entry) => { const failedEntry = entry as PerformanceResourceTiming; return failedEntry.responseEnd - failedEntry.startTime === 0; }) .map((entry) => ({ type: 'networkError', resource: entry.name, timestamp: new Date().toISOString(), })); // Combine collected errors with resource errors const allErrors = [...errorLog.errors, ...resourceErrors]; if (allErrors.length > 0) { logStore.logError('JavaScript Errors Found', { errors: allErrors, timestamp: new Date().toISOString(), }); toast.error(`Found ${allErrors.length} error(s)`); } else { toast.success('No errors found'); } // Update error log setErrorLog({ errors: allErrors, lastCheck: new Date().toISOString(), }); } catch (error) { toast.error('Failed to check for errors'); console.error('Failed to check for errors:', error); } finally { setLoading((prev) => ({ ...prev, errors: false })); } }; return (
{/* System Information */}

System Information

{systemInfo ? (
OS: {systemInfo.os}
Platform: {systemInfo.platform}
Architecture: {systemInfo.arch}
CPU Cores: {systemInfo.cpus}
Node Version: {systemInfo.node}
Network Type: {systemInfo.network.type} ({systemInfo.network.effectiveType})
Network Speed: {systemInfo.network.downlink}Mbps (RTT: {systemInfo.network.rtt}ms)
{systemInfo.battery && (
Battery: {systemInfo.battery.level.toFixed(1)}% {systemInfo.battery.charging ? '(Charging)' : ''}
)}
Storage: {(systemInfo.storage.usage / (1024 * 1024 * 1024)).toFixed(2)}GB /{' '} {(systemInfo.storage.quota / (1024 * 1024 * 1024)).toFixed(2)}GB
Memory Usage: {systemInfo.memory.used} / {systemInfo.memory.total} ({systemInfo.memory.percentage}%)
Browser: {systemInfo.browser.name} {systemInfo.browser.version}
Screen: {systemInfo.screen.width}x{systemInfo.screen.height} ({systemInfo.screen.pixelRatio}x)
Timezone: {systemInfo.time.timezone}
Language: {systemInfo.browser.language}
JS Heap: {(systemInfo.performance.memory.usedJSHeapSize / (1024 * 1024)).toFixed(1)}MB /{' '} {(systemInfo.performance.memory.totalJSHeapSize / (1024 * 1024)).toFixed(1)}MB ( {systemInfo.performance.memory.usagePercentage.toFixed(1)}%)
Page Load: {(systemInfo.performance.timing.loadTime / 1000).toFixed(2)}s
DOM Ready: {(systemInfo.performance.timing.domReadyTime / 1000).toFixed(2)}s
) : (
Loading system information...
)}
{/* Provider Status */}

Provider Status

{providerStatuses.map((provider) => (
{provider.name}
{provider.status}
))}
{/* Performance Metrics */}

Performance Metrics

{systemInfo && (
Page Load Time: {(systemInfo.performance.timing.loadTime / 1000).toFixed(2)}s
DOM Ready Time: {(systemInfo.performance.timing.domReadyTime / 1000).toFixed(2)}s
Request Time: {(systemInfo.performance.timing.requestTime / 1000).toFixed(2)}s
Redirect Time: {(systemInfo.performance.timing.redirectTime / 1000).toFixed(2)}s
JS Heap Usage: {(systemInfo.performance.memory.usedJSHeapSize / (1024 * 1024)).toFixed(1)}MB /{' '} {(systemInfo.performance.memory.totalJSHeapSize / (1024 * 1024)).toFixed(1)}MB
Heap Utilization: {systemInfo.performance.memory.usagePercentage.toFixed(1)}%
Navigation Type: {systemInfo.performance.navigation.type === 0 ? 'Navigate' : systemInfo.performance.navigation.type === 1 ? 'Reload' : systemInfo.performance.navigation.type === 2 ? 'Back/Forward' : 'Other'}
Redirects: {systemInfo.performance.navigation.redirectCount}
)}
{/* Error Check */}

Error Check

Checks for:
  • Unhandled JavaScript errors
  • Unhandled Promise rejections
  • Runtime exceptions
  • Network errors
Last Check: {loading.errors ? 'Checking...' : errorLog.lastCheck ? `Last checked ${new Date(errorLog.lastCheck).toLocaleString()} (${errorLog.errors.length} errors found)` : 'Click to check for errors'}
{errorLog.errors.length > 0 && (
Recent Errors:
{errorLog.errors.slice(0, 3).map((error, index) => (
{error.type === 'error' && `${error.message} (${error.filename}:${error.lineNumber})`} {error.type === 'unhandledRejection' && `Unhandled Promise Rejection: ${error.reason}`} {error.type === 'networkError' && `Network Error: Failed to load ${error.resource}`}
))} {errorLog.errors.length > 3 && (
And {errorLog.errors.length - 3} more errors...
)}
)}
); }