export interface DebugLogEntry { timestamp: string; level: "info" | "warn" | "error" | "debug"; source: "frontend" | "backend"; category: string; message: string; data?: unknown; } 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?: unknown, ): 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, target: event.target?.constructor.name, }); } public logMouseEvent(event: MouseEvent): void { const buttonNames: Record = { 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: unknown[], result?: unknown, 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?: unknown): 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; } }