import React, { useCallback, useEffect, useState, useMemo, useRef } from 'react'; import { useSettings } from '~/lib/hooks/useSettings'; import { toast } from 'react-toastify'; import { Switch } from '~/components/ui/Switch'; import { logStore, type LogEntry } from '~/lib/stores/logs'; import { useStore } from '@nanostores/react'; import { classNames } from '~/utils/classNames'; import { motion } from 'framer-motion'; import { settingsStyles } from '~/components/settings/settings.styles'; export default function EventLogsTab() { const {} = useSettings(); const showLogs = useStore(logStore.showLogs); const logs = useStore(logStore.logs); const [logLevel, setLogLevel] = useState('info'); const [autoScroll, setAutoScroll] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [, forceUpdate] = useState({}); const logsContainerRef = useRef(null); const [isScrolledToBottom, setIsScrolledToBottom] = useState(true); const filteredLogs = useMemo(() => { const allLogs = Object.values(logs); const filtered = allLogs.filter((log) => { const matchesLevel = !logLevel || log.level === logLevel || logLevel === 'all'; const matchesSearch = !searchQuery || log.message?.toLowerCase().includes(searchQuery.toLowerCase()) || JSON.stringify(log.details)?.toLowerCase()?.includes(searchQuery?.toLowerCase()); return matchesLevel && matchesSearch; }); return filtered.reverse(); }, [logs, logLevel, searchQuery]); // Effect to initialize showLogs useEffect(() => { logStore.showLogs.set(true); }, []); useEffect(() => { // System info logs logStore.logSystem('Application initialized', { version: process.env.NEXT_PUBLIC_APP_VERSION, environment: process.env.NODE_ENV, timestamp: new Date().toISOString(), userAgent: navigator.userAgent, }); // Debug logs for system state logStore.logDebug('System configuration loaded', { runtime: 'Next.js', features: ['AI Chat', 'Event Logging', 'Provider Management', 'Theme Support'], locale: navigator.language, timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone, }); // Performance metrics logStore.logSystem('Performance metrics', { deviceMemory: (navigator as any).deviceMemory || 'unknown', hardwareConcurrency: navigator.hardwareConcurrency, connectionType: (navigator as any).connection?.effectiveType || 'unknown', }); // Provider status logStore.logProvider('Provider status check', { availableProviders: ['OpenAI', 'Anthropic', 'Mistral', 'Ollama'], defaultProvider: 'OpenAI', status: 'operational', }); // Theme and accessibility logStore.logSystem('User preferences loaded', { theme: document.documentElement.dataset.theme || 'system', prefersReducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)').matches, prefersDarkMode: window.matchMedia('(prefers-color-scheme: dark)').matches, }); // Warning logs for potential issues logStore.logWarning('Resource usage threshold approaching', { memoryUsage: '75%', cpuLoad: '60%', timestamp: new Date().toISOString(), }); // Security checks logStore.logSystem('Security status', { httpsEnabled: window.location.protocol === 'https:', cookiesEnabled: navigator.cookieEnabled, storageQuota: 'checking...', }); // Error logs with detailed context logStore.logError('API connection failed', new Error('Connection timeout'), { endpoint: '/api/chat', retryCount: 3, lastAttempt: new Date().toISOString(), statusCode: 408, }); // Debug logs for development if (process.env.NODE_ENV === 'development') { logStore.logDebug('Development mode active', { debugFlags: true, mockServices: false, apiEndpoint: 'local', }); } }, []); // Scroll handling useEffect(() => { const container = logsContainerRef.current; if (!container) { return undefined; } const handleScroll = () => { const { scrollTop, scrollHeight, clientHeight } = container; const isBottom = Math.abs(scrollHeight - clientHeight - scrollTop) < 10; setIsScrolledToBottom(isBottom); }; container.addEventListener('scroll', handleScroll); const cleanup = () => { container.removeEventListener('scroll', handleScroll); }; return cleanup; }, []); // Auto-scroll effect useEffect(() => { const container = logsContainerRef.current; if (container && (autoScroll || isScrolledToBottom)) { container.scrollTop = 0; } }, [filteredLogs, autoScroll, isScrolledToBottom]); const handleClearLogs = useCallback(() => { if (confirm('Are you sure you want to clear all logs?')) { logStore.clearLogs(); toast.success('Logs cleared successfully'); forceUpdate({}); // Force a re-render after clearing logs } }, []); const handleExportLogs = useCallback(() => { try { const logText = logStore .getLogs() .map( (log) => `[${log.level.toUpperCase()}] ${log.timestamp} - ${log.message}${ log.details ? '\nDetails: ' + JSON.stringify(log.details, null, 2) : '' }`, ) .join('\n\n'); const blob = new Blob([logText], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `event-logs-${new Date().toISOString()}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); toast.success('Logs exported successfully'); } catch (error) { toast.error('Failed to export logs'); console.error('Export error:', error); } }, []); const getLevelIcon = (level: LogEntry['level']): string => { switch (level) { case 'info': return 'i-ph:info'; case 'warning': return 'i-ph:warning'; case 'error': return 'i-ph:x-circle'; case 'debug': return 'i-ph:bug'; default: return 'i-ph:circle'; } }; const getLevelColor = (level: LogEntry['level']) => { switch (level) { case 'info': return 'text-[#1389FD] dark:text-[#1389FD]'; case 'warning': return 'text-[#FFDB6C] dark:text-[#FFDB6C]'; case 'error': return 'text-[#EE4744] dark:text-[#EE4744]'; case 'debug': return 'text-[#77828D] dark:text-[#77828D]'; default: return 'text-bolt-elements-textPrimary'; } }; return (
{/* Title and Toggles Row */}

Event Logs

Track system events and debug information

Show Actions logStore.showLogs.set(checked)} />
Auto-scroll
{/* Controls Row */}
setSearchQuery(e.target.value)} className={classNames( 'w-full pl-9 pr-3 py-2 rounded-lg', 'bg-bolt-elements-background-depth-2 border border-bolt-elements-borderColor', 'text-sm text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary', 'focus:outline-none focus:ring-2 focus:ring-purple-500/30', 'group-hover:border-purple-500/30', 'transition-all duration-200', )} />
{showLogs && (
Export Logs
Clear Logs
)}
{filteredLogs.length === 0 ? (
No logs found
) : (
{filteredLogs.map((log, index) => (
{log.level.toUpperCase()} {new Date(log.timestamp).toLocaleString()} {log.message}
{log.details && ( {JSON.stringify(log.details, null, 2)} )}
))}
)}
); }