Skip to main content
Glama

Bybit MCP Server

by sammcj
DebugConsole.ts6.66 kB
/** * Debug Console Component - Real-time streaming log viewer */ import { logService, type LogEntry } from '../services/logService'; export class DebugConsole { private container: HTMLElement; private isVisible: boolean = false; private autoScroll: boolean = true; private filterLevels: Set<LogEntry['level']> = new Set(['log', 'info', 'warn', 'error']); private unsubscribe?: () => void; constructor(container: HTMLElement) { this.container = container; this.render(); this.setupEventListeners(); // Subscribe to log updates this.unsubscribe = logService.subscribe((logs) => { this.updateLogs(logs); }); } private render(): void { this.container.innerHTML = ` <div class="debug-console ${this.isVisible ? 'visible' : 'hidden'}"> <div class="debug-header"> <div class="debug-title"> <span class="debug-icon">🔍</span> <span>Debug Console</span> <span class="debug-count">(${logService.getLogs().length})</span> </div> <div class="debug-controls"> <div class="debug-filters"> <label><input type="checkbox" data-level="log" ${this.filterLevels.has('log') ? 'checked' : ''}> Log</label> <label><input type="checkbox" data-level="info" ${this.filterLevels.has('info') ? 'checked' : ''}> Info</label> <label><input type="checkbox" data-level="warn" ${this.filterLevels.has('warn') ? 'checked' : ''}> Warn</label> <label><input type="checkbox" data-level="error" ${this.filterLevels.has('error') ? 'checked' : ''}> Error</label> </div> <button class="debug-btn" data-action="clear">Clear</button> <button class="debug-btn" data-action="export">Export</button> <button class="debug-btn" data-action="scroll-toggle"> ${this.autoScroll ? '📌' : '📌'} </button> <button class="debug-btn debug-toggle" data-action="toggle"> ${this.isVisible ? '▼' : '▲'} </button> </div> </div> <div class="debug-content"> <div class="debug-logs" id="debug-logs"></div> </div> </div> `; this.updateLogs(logService.getLogs()); } private setupEventListeners(): void { this.container.addEventListener('click', (e) => { const target = e.target as HTMLElement; const action = target.getAttribute('data-action'); switch (action) { case 'toggle': this.toggle(); break; case 'clear': logService.clearLogs(); break; case 'export': this.exportLogs(); break; case 'scroll-toggle': this.autoScroll = !this.autoScroll; target.textContent = this.autoScroll ? '📌' : '📌'; target.title = this.autoScroll ? 'Auto-scroll enabled' : 'Auto-scroll disabled'; break; } }); this.container.addEventListener('change', (e) => { const target = e.target as HTMLInputElement; const level = target.getAttribute('data-level') as LogEntry['level']; if (level) { if (target.checked) { this.filterLevels.add(level); } else { this.filterLevels.delete(level); } this.updateLogs(logService.getLogs()); } }); } private updateLogs(logs: LogEntry[]): void { const logsContainer = this.container.querySelector('#debug-logs') as HTMLElement; if (!logsContainer) return; // Filter logs by selected levels const filteredLogs = logs.filter(log => this.filterLevels.has(log.level)); // Update count const countElement = this.container.querySelector('.debug-count') as HTMLElement; if (countElement) { countElement.textContent = `(${filteredLogs.length}/${logs.length})`; } // Render logs logsContainer.innerHTML = filteredLogs.map(log => this.renderLogEntry(log)).join(''); // Auto-scroll to bottom if (this.autoScroll && this.isVisible) { logsContainer.scrollTop = logsContainer.scrollHeight; } } private renderLogEntry(log: LogEntry): string { const time = new Date(log.timestamp).toLocaleTimeString(); const levelClass = `debug-log-${log.level}`; const source = log.source ? ` <span class="debug-source">[${log.source}]</span>` : ''; let dataHtml = ''; if (log.data) { const dataStr = typeof log.data === 'object' ? JSON.stringify(log.data, null, 2) : String(log.data); dataHtml = `<div class="debug-data">${this.escapeHtml(dataStr)}</div>`; } return ` <div class="debug-log-entry ${levelClass}"> <div class="debug-log-header"> <span class="debug-time">${time}</span> <span class="debug-level">${log.level.toUpperCase()}</span> ${source} </div> <div class="debug-message">${this.escapeHtml(log.message)}</div> ${dataHtml} </div> `; } private escapeHtml(text: string): string { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } private exportLogs(): void { const logs = logService.exportLogs(); const blob = new Blob([logs], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `debug-logs-${new Date().toISOString().slice(0, 19)}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } public toggle(): void { this.isVisible = !this.isVisible; const debugConsole = this.container.querySelector('.debug-console') as HTMLElement; const toggleBtn = this.container.querySelector('.debug-toggle') as HTMLElement; if (debugConsole) { debugConsole.className = `debug-console ${this.isVisible ? 'visible' : 'hidden'}`; } if (toggleBtn) { toggleBtn.textContent = this.isVisible ? '▼' : '▲'; } // Auto-scroll when opening if (this.isVisible && this.autoScroll) { setTimeout(() => { const logsContainer = this.container.querySelector('#debug-logs') as HTMLElement; if (logsContainer) { logsContainer.scrollTop = logsContainer.scrollHeight; } }, 100); } } public show(): void { if (!this.isVisible) { this.toggle(); } } public hide(): void { if (this.isVisible) { this.toggle(); } } public destroy(): void { if (this.unsubscribe) { this.unsubscribe(); } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sammcj/bybit-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server