From 5e10723021de8a90c8e41b7a4ff6481910b3c213 Mon Sep 17 00:00:00 2001 From: nate Date: Tue, 24 Mar 2026 17:59:06 +0400 Subject: [PATCH] feat: ping body syntax highlighting --- apps/web/src/views/detail.ejs | 49 +++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) 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 += '
'; if (ping.id) { - html += '
Response Body
'; + html += `
Response Body${ctLabel}
`; html += '
Loading...
'; } html += '
'; @@ -236,12 +280,13 @@ // Fetch body asynchronously if (ping.id) { + const ct = getContentType(headers); api(`/pings/${ping.id}/body`).then(data => { const el = document.getElementById('ping-body-content'); if (!el) return; if (data.body) { el.className = 'bg-gray-800/50 border border-border-subtle rounded-lg px-3 py-2 text-xs font-mono text-gray-300 whitespace-pre-wrap break-all max-h-80 overflow-y-auto'; - el.textContent = data.body; + el.innerHTML = highlightBody(data.body, ct); } else { el.textContent = 'No body stored'; }