diff --git a/apps/web/src/views/detail.ejs b/apps/web/src/views/detail.ejs index de1bced..045ca66 100644 --- a/apps/web/src/views/detail.ejs +++ b/apps/web/src/views/detail.ejs @@ -173,6 +173,48 @@ const _initialQuery = <%~ JSON.stringify(m.query || null) %>; <%~ include('./partials/monitor-form-js') %> + // ── Syntax highlighting ──────────────────────────────────────── + function highlightJson(str) { + try { str = JSON.stringify(JSON.parse(str), null, 2); } catch {} + return escapeHtml(str).replace( + /("(?:\\.|[^"\\])*")\s*:/g, + '$1:' + ).replace( + /:\s*("(?:\\.|[^"\\])*")/g, + ': $1' + ).replace( + /:\s*(true|false)/g, + ': $1' + ).replace( + /:\s*(null)/g, + ': $1' + ).replace( + /:\s*(-?\d+\.?\d*(?:[eE][+-]?\d+)?)/g, + ': $1' + ); + } + + function highlightXml(str) { + return escapeHtml(str) + .replace(/<(\/?)([\w:-]+)/g, '<$1$2') + .replace(/([\w:-]+)(=)("[^&]*")/g, '$1$2$3'); + } + + function highlightBody(body, contentType) { + const ct = (contentType || '').toLowerCase(); + if (ct.includes('json')) return highlightJson(body); + if (ct.includes('xml') || ct.includes('html')) return highlightXml(body); + return escapeHtml(body); + } + + function getContentType(headers) { + if (!headers) return ''; + for (const [k, v] of Object.entries(headers)) { + if (k.toLowerCase() === 'content-type') return String(v); + } + return ''; + } + // ── Ping detail modal ────────────────────────────────────────── function openPingModal(ping) { const modalBody = document.getElementById('ping-modal-body'); @@ -223,9 +265,11 @@ } // Response body — loaded on demand + const contentType = getContentType(headers); + const ctLabel = contentType ? ` (${escapeHtml(contentType.split(';')[0].trim())})` : ''; html += '