/** * DebugControl - System Debug Information and Message Display Control * * Provides comprehensive debugging capabilities including system message display, * error tracking, performance monitoring, and development tools. Essential for * troubleshooting and development workflows within the TestDrive-JSUI environment. * * Features: * - Real-time debug message display with categorization * - Error tracking with stack trace information * - Performance metrics and timing measurements * - System information display (browser, viewport, etc.) * - Message filtering and search capabilities * - Export functionality for debug logs * - Integration with MarkitectDebugSystem * * Dependencies: * - ControlBase (base control functionality) * - MarkitectDebugSystem (optional, for enhanced debugging) */ /** * DebugControl - Development and debugging information control * * This control serves as a central hub for all debugging activities, * providing developers with essential information for troubleshooting * and performance optimization. */ class DebugControl extends ControlBase { constructor() { super(); // Configure for debug functionality this.config = { icon: 'πŸ›', title: 'Debug', className: 'debug-control', defaultContent: 'Debug information loading...', ariaLabel: 'Debug Information Control', position: 'w' // West positioning }; // Debug control state this.messages = []; this.maxMessages = 100; this.messageFilter = 'all'; // 'all', 'error', 'warn', 'info', 'debug' this.autoScroll = true; this.isRecording = true; this.startTime = Date.now(); this.performanceMarks = new Map(); this.initializeDebugCapture(); } /** * Initialize debug message capture */ initializeDebugCapture() { return this.safeOperation(() => { // Capture console messages this.originalConsole = { log: console.log, error: console.error, warn: console.warn, info: console.info, debug: console.debug }; // Override console methods to capture messages console.log = (...args) => { this.originalConsole.log(...args); this.addDebugMessage('LOG', args.join(' '), 'info'); }; console.error = (...args) => { this.originalConsole.error(...args); this.addDebugMessage('ERROR', args.join(' '), 'error'); }; console.warn = (...args) => { this.originalConsole.warn(...args); this.addDebugMessage('WARN', args.join(' '), 'warn'); }; console.info = (...args) => { this.originalConsole.info(...args); this.addDebugMessage('INFO', args.join(' '), 'info'); }; console.debug = (...args) => { this.originalConsole.debug(...args); this.addDebugMessage('DEBUG', args.join(' '), 'debug'); }; // Capture global errors window.addEventListener('error', (event) => { this.addDebugMessage('ERROR', `${event.message} at ${event.filename}:${event.lineno}`, 'error'); }); // Capture unhandled promise rejections window.addEventListener('unhandledrejection', (event) => { this.addDebugMessage('PROMISE_REJECT', `Unhandled promise rejection: ${event.reason}`, 'error'); }); }, null, 'initializeDebugCapture'); } /** * Add a debug message to the log */ addDebugMessage(category, message, level = 'info') { return this.safeOperation(() => { if (!this.isRecording) return; const debugMessage = { id: Date.now() + Math.random(), timestamp: Date.now(), category, message, level, displayTime: new Date().toLocaleTimeString(), relativeTime: Date.now() - this.startTime }; this.messages.push(debugMessage); // Limit message history if (this.messages.length > this.maxMessages) { this.messages.shift(); } // Update display if visible if (this.element && this.isExpanded) { this.updateMessageDisplay(); } }, null, 'addDebugMessage'); } /** * Get messages filtered by current filter setting */ getFilteredMessages() { if (this.messageFilter === 'all') { return this.messages; } return this.messages.filter(msg => msg.level === this.messageFilter); } /** * Generate system information HTML */ generateSystemInfoHTML() { return this.safeOperation(() => { const systemInfo = { userAgent: navigator.userAgent, viewport: `${window.innerWidth}x${window.innerHeight}`, screen: `${screen.width}x${screen.height}`, colorDepth: screen.colorDepth, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, language: navigator.language, cookieEnabled: navigator.cookieEnabled, onlineStatus: navigator.onLine ? 'Online' : 'Offline', protocol: window.location.protocol, memory: performance.memory ? `Used: ${Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)}MB` : 'Not available' }; // Get Markitect version from config or default const markitectVersion = window.markitectConfig?.version || 'Unknown'; return `
Markitect: ${markitectVersion}
Viewport: ${systemInfo.viewport}
Screen: ${systemInfo.screen}
Memory: ${systemInfo.memory}
Language: ${systemInfo.language}
Status: ${systemInfo.onlineStatus}
Protocol: ${systemInfo.protocol}
`; }, '', 'generateSystemInfoHTML'); } /** * Generate performance metrics HTML */ generatePerformanceHTML() { return this.safeOperation(() => { const timing = performance.timing; const navigation = performance.getEntriesByType('navigation')[0]; const metrics = { pageLoad: timing.loadEventEnd - timing.navigationStart, domReady: timing.domContentLoadedEventEnd - timing.navigationStart, firstByte: timing.responseStart - timing.navigationStart, uptime: Date.now() - this.startTime, messagesCount: this.messages.length }; return `
Performance Metrics:
Page Load: ${metrics.pageLoad}ms
DOM Ready: ${metrics.domReady}ms
First Byte: ${metrics.firstByte}ms
Session Time: ${Math.round(metrics.uptime / 1000)}s
Debug Messages: ${metrics.messagesCount}
`; }, '', 'generatePerformanceHTML'); } /** * Generate debug messages HTML */ generateMessagesHTML() { return this.safeOperation(() => { const filteredMessages = this.getFilteredMessages(); if (filteredMessages.length === 0) { return `
No ${this.messageFilter === 'all' ? '' : this.messageFilter + ' '}messages yet
`; } const messagesHTML = filteredMessages.slice(-20).map(msg => { const levelColors = { error: '#dc3545', warn: '#ffc107', info: '#17a2b8', debug: '#6c757d' }; const backgroundColor = levelColors[msg.level] || '#6c757d'; const textColor = msg.level === 'warn' ? '#000' : '#fff'; return `
${msg.category} ${msg.displayTime}
${msg.message}
`; }).join(''); return `
${messagesHTML}
`; }, '

Error displaying messages

', 'generateMessagesHTML'); } /** * Generate control buttons HTML */ generateControlButtonsHTML() { return `
`; } /** * Generate filter controls HTML */ generateFilterControlsHTML() { const filters = ['all', 'error', 'warn', 'info', 'debug']; const filterButtons = filters.map(filter => { const isActive = this.messageFilter === filter; return ` `; }).join(''); return `
Filter:
${filterButtons}
`; } /** * Update the message display */ updateMessageDisplay() { return this.safeOperation(() => { const messagesContainer = this.element?.querySelector('.messages-container'); if (messagesContainer) { const parent = messagesContainer.parentElement; parent.innerHTML = this.generateMessagesHTML(); // Auto-scroll to bottom if enabled if (this.autoScroll) { const newContainer = parent.querySelector('.messages-container'); if (newContainer) { newContainer.scrollTop = newContainer.scrollHeight; } } } }, null, 'updateMessageDisplay'); } /** * Clear all debug messages */ clearMessages() { this.messages = []; if (window.MarkitectDebugSystem) { window.MarkitectDebugSystem.clearMessages(); } this.buildContent(); } /** * Export debug messages to file */ exportMessages() { return this.safeOperation(() => { const exportData = { timestamp: new Date().toISOString(), session: { startTime: new Date(this.startTime).toISOString(), duration: Date.now() - this.startTime, messageCount: this.messages.length }, system: { userAgent: navigator.userAgent, viewport: `${window.innerWidth}x${window.innerHeight}`, url: window.location.href }, messages: this.messages }; const dataStr = JSON.stringify(exportData, null, 2); const dataBlob = new Blob([dataStr], { type: 'application/json' }); const url = URL.createObjectURL(dataBlob); const link = document.createElement('a'); link.href = url; link.download = `debug-log-${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); this.addDebugMessage('EXPORT', 'Debug log exported successfully', 'info'); }, null, 'exportMessages'); } /** * Toggle message recording */ toggleRecording() { this.isRecording = !this.isRecording; this.buildContent(); this.addDebugMessage('CONTROL', `Recording ${this.isRecording ? 'started' : 'paused'}`, 'info'); } /** * Add a test message */ addTestMessage() { const testMessages = [ { category: 'TEST', message: 'This is a test info message', level: 'info' }, { category: 'TEST', message: 'This is a test warning message', level: 'warn' }, { category: 'TEST', message: 'This is a test error message', level: 'error' }, { category: 'TEST', message: 'This is a test debug message', level: 'debug' } ]; const randomMessage = testMessages[Math.floor(Math.random() * testMessages.length)]; this.addDebugMessage(randomMessage.category, randomMessage.message, randomMessage.level); } /** * Set message filter */ setMessageFilter(filter) { this.messageFilter = filter; this.buildContent(); } /** * Generate debug control content (called by base class buildContent) */ generateContent() { return this.safeOperation(() => { return ` ${this.generateSystemInfoHTML()} ${this.generatePerformanceHTML()} ${this.generateFilterControlsHTML()} ${this.generateMessagesHTML()} ${this.generateControlButtonsHTML()}
Recording: ${this.isRecording ? '🟒 Active' : 'πŸ”΄ Paused'} | Filter: ${this.messageFilter.toUpperCase()} | Messages: ${this.getFilteredMessages().length}/${this.messages.length}
`; }, 'Error generating debug content', 'generateContent'); } /** * Override buildContent to add control reference */ buildContent() { super.buildContent(); // Store reference to this control for onclick handlers if (this.element) { this.element.debugControl = this; } } /** * Clean up resources when control is destroyed */ destroy() { // Restore original console methods if (this.originalConsole) { console.log = this.originalConsole.log; console.error = this.originalConsole.error; console.warn = this.originalConsole.warn; console.info = this.originalConsole.info; console.debug = this.originalConsole.debug; } super.destroy(); } } // Export for module systems or attach to global for direct usage if (typeof module !== 'undefined' && module.exports) { module.exports = DebugControl; } else { window.DebugControl = DebugControl; }