export interface DebugLogEntry { timestamp: string; level: 'info' | 'warn' | 'error' | 'debug'; source: 'frontend' | 'backend'; category: string; message: string; data?: any; } export class DebugConsoleService { private logs: DebugLogEntry[] = []; private maxLogs = 1000; private isVisible = false; private debugElement: HTMLElement | null = null; private logsContainer: HTMLElement | null = null; constructor() { this.createDebugUI(); this.setupKeyboardShortcut(); this.interceptConsole(); } private createDebugUI(): void { // Create debug panel this.debugElement = document.createElement('div'); this.debugElement.id = 'debug-console'; this.debugElement.style.cssText = ` position: fixed; top: 0; right: 0; width: 50%; height: 100%; background: rgba(0, 0, 0, 0.95); color: #00ff00; font-family: 'Courier New', monospace; font-size: 12px; z-index: 10000; display: none; border-left: 2px solid #00ff00; `; // Create header const header = document.createElement('div'); header.style.cssText = ` padding: 10px; background: rgba(0, 255, 0, 0.1); border-bottom: 1px solid #00ff00; font-weight: bold; display: flex; justify-content: space-between; align-items: center; `; header.innerHTML = ` 🐛 DUMBER DEBUG CONSOLE
F11: Inspect Services | F12: Toggle
`; // Create logs container this.logsContainer = document.createElement('div'); this.logsContainer.style.cssText = ` height: calc(100% - 50px); overflow-y: auto; padding: 10px; `; this.debugElement.appendChild(header); this.debugElement.appendChild(this.logsContainer); document.body.appendChild(this.debugElement); // Setup button handlers header.querySelector('#clear-logs')?.addEventListener('click', () => this.clearLogs()); header.querySelector('#close-debug')?.addEventListener('click', () => this.hide()); } private setupKeyboardShortcut(): void { document.addEventListener('keydown', (event) => { // F12 to toggle debug console if (event.key === 'F12') { event.preventDefault(); this.toggle(); } }); } private interceptConsole(): void { const originalConsole = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug }; console.log = (...args) => { this.addLog('info', 'frontend', 'console', args.join(' ')); originalConsole.log(...args); }; console.warn = (...args) => { this.addLog('warn', 'frontend', 'console', args.join(' ')); originalConsole.warn(...args); }; console.error = (...args) => { this.addLog('error', 'frontend', 'console', args.join(' ')); originalConsole.error(...args); }; console.info = (...args) => { this.addLog('info', 'frontend', 'console', args.join(' ')); originalConsole.info(...args); }; console.debug = (...args) => { this.addLog('debug', 'frontend', 'console', args.join(' ')); originalConsole.debug(...args); }; } public addLog(level: DebugLogEntry['level'], source: DebugLogEntry['source'], category: string, message: string, data?: any): void { const entry: DebugLogEntry = { timestamp: new Date().toISOString().split('T')[1].split('.')[0], // HH:MM:SS format level, source, category, message, data }; this.logs.push(entry); // Keep only the last maxLogs entries if (this.logs.length > this.maxLogs) { this.logs.shift(); } this.updateDisplay(); } private updateDisplay(): void { if (!this.logsContainer) return; const logHTML = this.logs.map(log => { const levelColor = { info: '#00ff00', warn: '#ffff00', error: '#ff0000', debug: '#00ffff' }[log.level]; const sourceIcon = log.source === 'backend' ? '🖥️' : '🌐'; return `
[${log.timestamp}] ${sourceIcon} ${log.level.toUpperCase()} [${log.category}] ${log.message} ${log.data ? `
${JSON.stringify(log.data, null, 2)}
` : ''}
`; }).join(''); this.logsContainer.innerHTML = logHTML; // Auto-scroll to bottom this.logsContainer.scrollTop = this.logsContainer.scrollHeight; } public logKeyboardEvent(event: KeyboardEvent): void { const modifiers = []; if (event.ctrlKey) modifiers.push('Ctrl'); if (event.shiftKey) modifiers.push('Shift'); if (event.altKey) modifiers.push('Alt'); if (event.metaKey) modifiers.push('Meta'); const shortcut = modifiers.length > 0 ? `${modifiers.join('+')}+${event.key}` : event.key; this.addLog('info', 'frontend', 'keyboard', `Key pressed: ${shortcut}`, { key: event.key, code: event.code, modifiers: modifiers, target: event.target?.constructor.name }); } public logMouseEvent(event: MouseEvent): void { const buttonNames = { 0: 'Left', 1: 'Middle', 2: 'Right', 3: 'Back', 4: 'Forward' }; this.addLog('info', 'frontend', 'mouse', `Mouse ${event.type}: ${buttonNames[event.button] || `Button${event.button}`}`, { button: event.button, clientX: event.clientX, clientY: event.clientY, target: event.target?.constructor.name }); } public logServiceCall(serviceName: string, methodName: string, params: any[], result?: any, error?: Error): void { if (error) { this.addLog('error', 'frontend', 'service', `${serviceName}.${methodName}() failed`, { params, error: error.message }); } else { this.addLog('info', 'frontend', 'service', `${serviceName}.${methodName}() called`, { params, result }); } } public logBackendMessage(message: string, data?: any): void { this.addLog('info', 'backend', 'service', message, data); } public show(): void { if (this.debugElement) { this.debugElement.style.display = 'block'; this.isVisible = true; this.addLog('info', 'frontend', 'debug', 'Debug console opened'); } } public hide(): void { if (this.debugElement) { this.debugElement.style.display = 'none'; this.isVisible = false; } } public toggle(): void { if (this.isVisible) { this.hide(); } else { this.show(); } } public clearLogs(): void { this.logs = []; this.updateDisplay(); this.addLog('info', 'frontend', 'debug', 'Logs cleared'); } public isDebugVisible(): boolean { return this.isVisible; } }