From 6aa24b976952a698c673d350977721b27193183d Mon Sep 17 00:00:00 2001 From: Agando Date: Tue, 16 Jun 2026 06:01:21 +0200 Subject: auto: 2026-06-16_06:00 changes --- modules/ext/debug-panel.js | 167 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 modules/ext/debug-panel.js (limited to 'modules/ext') diff --git a/modules/ext/debug-panel.js b/modules/ext/debug-panel.js new file mode 100644 index 0000000..8c37253 --- /dev/null +++ b/modules/ext/debug-panel.js @@ -0,0 +1,167 @@ +class DebugPanel { + constructor() { + this.commands = {}; + this.terminal = null; + this._logs = []; + this._original = {}; + this._panel = null; + this._body = null; + this._visible = false; + this.MAX_LOGS = 500; + } + requirements() { + return { met: true, missing: [] }; + } + getHelpInfo() { + return { 'debug': 'debug — Toggle the debug console panel' }; + } + registerCommands() { + this.commands['debug'] = () => this.toggle(); + } + seed() { + this._intercept(); + } + init() {} + _intercept() { + const self = this; + // Console + for (const m of ['log','warn','error','info']) { + this._original[m] = console[m]; + console[m] = function(...args) { + self._add(m, args); + self._original[m].apply(console, args); + }; + } + // window.onerror + this._original.onerror = window.onerror; + window.onerror = function(msg, url, line, col, err) { + self._add('error', [`${msg} (${url}:${line}:${col})`]); + if (self._original.onerror) self._original.onerror(msg, url, line, col, err); + }; + // Unhandled rejections + window.addEventListener('unhandledrejection', function(e) { + self._add('error', ['Unhandled Promise: ' + (e.reason?.message || e.reason)]); + }); + // Fetch + this._original.fetch = window.fetch; + window.fetch = function(...args) { + const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || '?'; + const method = (args[1]?.method || 'GET'); + const start = performance.now(); + return self._original.fetch.apply(window, args) + .then(function(resp) { + const ms = (performance.now() - start).toFixed(0); + const lvl = resp.status >= 400 ? 'error' : resp.status >= 300 ? 'warn' : 'log'; + self._add(lvl, ['🌐 ' + method + ' ' + url + ' → ' + resp.status + ' (' + ms + 'ms)']); + return resp; + }) + .catch(function(err) { + const ms = (performance.now() - start).toFixed(0); + self._add('error', ['🌐 ' + method + ' ' + url + ' FAIL (' + ms + 'ms): ' + err.message]); + throw err; + }); + }; + } + _add(level, args) { + const text = args.map(function(a) { + if (typeof a === 'object') try { return JSON.stringify(a, null, 2); } catch(e) { return String(a); } + return String(a); + }).join(' '); + this._logs.push({ level, text, time: new Date().toLocaleTimeString() }); + if (this._logs.length > this.MAX_LOGS) this._logs.shift(); + if (this._visible) this._appendEntry(this._logs[this._logs.length - 1]); + } + toggle() { + if (!this._panel) this._build(); + this._visible = !this._visible; + this._panel.style.display = this._visible ? 'flex' : 'none'; + if (this._visible) { + this._body.innerHTML = ''; + for (const e of this._logs) this._appendEntry(e); + this._body.scrollTop = this._body.scrollHeight; + } + } + _build() { + const p = document.createElement('div'); + p.id = 'debug-panel'; + p.style.cssText = 'position:fixed;bottom:0;left:0;right:0;height:40vh;background:#0d0d12;border-top:2px solid #1e1e2a;z-index:9999;display:none;flex-direction:column;font-family:monospace;font-size:12px;'; + // Header + const h = document.createElement('div'); + h.style.cssText = 'display:flex;align-items:center;padding:4px 10px;background:#16161e;border-bottom:1px solid #1e1e2a;gap:6px;flex-shrink:0;'; + h.innerHTML = '🐞 Debug Console'; + // Copy All + const ca = document.createElement('button'); + ca.textContent = '📋 Copy All'; + ca.onclick = () => this._copyAll(); + ca.style.cssText = 'background:#1e1e2a;color:#e8e8f0;border:1px solid #2a2a3a;padding:2px 10px;border-radius:6px;cursor:pointer;font-size:11px;font-family:inherit;'; + // Clear + const cl = document.createElement('button'); + cl.textContent = '🗑 Clear'; + cl.onclick = () => { this._logs = []; if (this._body) this._body.innerHTML = ''; }; + cl.style.cssText = ca.style.cssText; + // Close + const cx = document.createElement('button'); + cx.textContent = '✕'; + cx.onclick = () => this.toggle(); + cx.style.cssText = 'background:none;color:#8888a0;border:none;cursor:pointer;margin-left:auto;font-size:18px;padding:0 4px;'; + h.appendChild(ca); + h.appendChild(cl); + h.appendChild(cx); + p.appendChild(h); + // Body + this._body = document.createElement('div'); + this._body.style.cssText = 'flex:1;overflow-y:auto;padding:4px 10px;background:#111118;'; + p.appendChild(this._body); + document.body.appendChild(p); + this._panel = p; + } + _appendEntry(e) { + const colors = { error: '#ff6bcb', warn: '#ffa500', log: '#00e5c8', info: '#7c6bff' }; + const line = document.createElement('div'); + line.style.cssText = 'padding:3px 4px;border-bottom:1px solid #1a1a26;display:flex;align-items:flex-start;gap:6px;'; + // Badge + const b = document.createElement('span'); + b.textContent = e.level.toUpperCase(); + b.style.cssText = 'color:' + (colors[e.level] || '#888') + ';font-weight:700;font-size:10px;width:40px;flex-shrink:0;'; + // Timestamp + const ts = document.createElement('span'); + ts.textContent = e.time; + ts.style.cssText = 'color:#555;font-size:10px;width:70px;flex-shrink:0;'; + // Message + const m = document.createElement('span'); + m.textContent = e.text; + m.style.cssText = 'color:#c8c8d0;flex:1;word-break:break-all;white-space:pre-wrap;'; + // Copy + const cp = document.createElement('button'); + cp.textContent = 'Copy'; + cp.onclick = function() { + var ta = document.createElement('textarea'); + ta.value = e.text; + ta.style.cssText = 'position:fixed;left:-999px;top:0;'; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); + cp.textContent = '✓'; + setTimeout(function() { cp.textContent = 'Copy'; }, 1500); + }; + cp.style.cssText = 'background:#1e1e2a;color:#8888a0;border:1px solid #2a2a3a;border-radius:4px;cursor:pointer;font-size:10px;padding:1px 6px;flex-shrink:0;font-family:inherit;'; + line.appendChild(b); + line.appendChild(ts); + line.appendChild(m); + line.appendChild(cp); + this._body.appendChild(line); + this._body.scrollTop = this._body.scrollHeight; + } + _copyAll() { + const text = this._logs.map(function(e) { return '[' + e.level.toUpperCase() + '] ' + e.time + ' ' + e.text; }).join('\n'); + var ta = document.createElement('textarea'); + ta.value = text; + ta.style.cssText = 'position:fixed;left:-999px;top:0;'; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); + if (this.terminal) this.terminal.print('📋 Debug log copied (' + this._logs.length + ' entries)', 'success'); + } +} \ No newline at end of file -- cgit v1.2.3