diff --git a/DebugProbe.AspNetCore.Tests/Rendering/HtmlRendererTests.cs b/DebugProbe.AspNetCore.Tests/Rendering/HtmlRendererTests.cs index f343bd5..b2456e4 100644 --- a/DebugProbe.AspNetCore.Tests/Rendering/HtmlRendererTests.cs +++ b/DebugProbe.AspNetCore.Tests/Rendering/HtmlRendererTests.cs @@ -46,16 +46,16 @@ public void Details_page_renders_captured_values() } [Fact] - public void Payload_badges_render_for_json_empty_text_and_hidden_payloads() + public void Payload_groups_render_for_json_empty_text_and_hidden_payloads() { var jsonHtml = HtmlRenderer.RenderDetailsPage(CreateEntry(), CreateEnvironment(), "{\"ok\":true}", "plain"); var emptyHtml = HtmlRenderer.RenderDetailsPage(CreateEntry(), CreateEnvironment(), "", ""); var hiddenHtml = HtmlRenderer.RenderDetailsPage(CreateEntry(), CreateEnvironment(), "[Body too large]", "[Body too large]"); - Assert.Contains("payload-json", jsonHtml); - Assert.Contains("payload-text", jsonHtml); - Assert.Contains("payload-empty", emptyHtml); - Assert.Contains("payload-hidden", hiddenHtml); + Assert.Contains("Request", jsonHtml); + Assert.Contains("Response", jsonHtml); + + Assert.Contains("[Body too large]", hiddenHtml); } [Fact] diff --git a/DebugProbe.AspNetCore/Assets/css/debugprobe.css b/DebugProbe.AspNetCore/Assets/css/debugprobe.css index 32bdda5..e411cfe 100644 --- a/DebugProbe.AspNetCore/Assets/css/debugprobe.css +++ b/DebugProbe.AspNetCore/Assets/css/debugprobe.css @@ -28,6 +28,10 @@ h3 { margin-bottom: 10px; } +h4 { + margin: 0; +} + /* ========================= Layout ========================= */ @@ -159,18 +163,98 @@ tr:hover { Section Titles ========================= */ +.payload-group { + margin-top: 24px; + padding: 18px; + background: #fff; + border: 1px solid #e8e8e8; + border-left-width: 5px; + border-radius: 8px; +} + +.request-group { + border-left-color: #2f80ed; +} + +.response-group { + border-left-color: #27ae60; +} + +.response-group.response-error { + border-left-color: #e74c3c; +} + +.compare-group { + border-left-color: #f39c12; +} + +.compare-description { + margin-top: 4px; + color: #777; + font-size: 0.9rem; +} + +.payload-group-title { + margin-bottom: 16px; + padding-bottom: 12px; + border-bottom: 1px solid #f0f0f0; +} + +.payload-group-title h3 { + margin: 0; +} + .section-title { display: flex; align-items: center; gap: 10px; - margin-top: 25px; - margin-bottom: 10px; + margin-top: 18px; + margin-bottom: 8px; +} + +.payload-group-title + .section-title { + margin-top: 0; } -.section-title h3 { +.section-title h3, +.section-title h4 { margin: 0; } +.section-title h4 { + color: #555; + font-size: 13px; + font-weight: 700; + letter-spacing: 0; +} + +.compare-controls { + display: flex; + flex-wrap: wrap; + gap: 8px; + padding-bottom: 14px; +} + +.compare-controls input { + min-width: 220px; + flex: 1; +} + +@media (max-width: 640px) { + .container { + padding: 14px; + } + + .payload-group { + padding: 14px; + } + + .compare-controls input, + .compare-controls button { + width: 100%; + } +} + /* ========================= Code Blocks ========================= */ @@ -239,8 +323,7 @@ pre { } .code-badge, -.status, -.diff-badge { +.status { display: inline-flex; align-items: center; justify-content: center; @@ -253,40 +336,6 @@ pre { font-size: 12px; } -.payload-json { - background: #183d24; - color: #6ddf8b; -} - -.payload-invalid-json { - background: #4a1717; - color: #ff8a8a; -} - -.payload-text { - background: #4a3112; - color: #ffb84d; -} - -.payload-url { - background: #1d3557; - color: #7cc7ff; -} - -.payload-empty { - background: #444; - color: #ddd; -} - -.payload-hidden { - display: none; -} - -.diff-badge { - background: #4a1717; - color: #ff8a8a; -} - /* ========================= Diff ========================= */ diff --git a/DebugProbe.AspNetCore/Assets/html/_shared/layout.html b/DebugProbe.AspNetCore/Assets/html/_shared/layout.html index 51d2a1f..362b9cf 100644 --- a/DebugProbe.AspNetCore/Assets/html/_shared/layout.html +++ b/DebugProbe.AspNetCore/Assets/html/_shared/layout.html @@ -10,10 +10,9 @@ {{env_block}} - {{content}} - \ No newline at end of file + diff --git a/DebugProbe.AspNetCore/Assets/html/details.html b/DebugProbe.AspNetCore/Assets/html/details.html index 8259448..5a49097 100644 --- a/DebugProbe.AspNetCore/Assets/html/details.html +++ b/DebugProbe.AspNetCore/Assets/html/details.html @@ -7,7 +7,7 @@

{{method}} {{path}}

-
Overview
+
Transaction Overview
Status @@ -25,7 +25,7 @@

{{method}} {{path}}

- Time + UTC {{time}}
@@ -51,7 +51,7 @@

{{method}} {{path}}

-
Environment
+
Environment Overview
Environment @@ -91,91 +91,76 @@

{{method}} {{path}}

-
-

Request URL

- -
- - URL - +
+
+

Request

-
-
- -
{{requestUrl}}
-
- -
-

Request Headers

-
- - HEADERS - +
+

URL

-
- -
- -
{{requestHeaders}}
-
- -
-

Request Body

- -
- - {{requestType}} - +
+ +
{{requestUrl}}
-
-
- -
{{request}}
-
-
-

Response Headers

+
+

Headers

+
-
- - HEADERS - +
+ +
{{requestHeaders}}
-
-
- -
{{responseHeaders}}
-
+
+

Body

+
+
+ +
{{request}}
+
+ -
-

Response Body

+
+
+

Response

+
-
- - {{responseType}} - +
+

Headers

+
- - {{responseStatusCode}} - +
+ +
{{responseHeaders}}
-
-
- -
{{response}}
-
-

Compare

+
+

Body

+
+
+ +
{{response}}
+
+
+ +
+
+

Compare

+

+ Compare this trace with another environment/server. +

+
-
- - - -
+
+ + + +
-
+
+
diff --git a/DebugProbe.AspNetCore/Assets/js/debugprobe-compare-renderer.js b/DebugProbe.AspNetCore/Assets/js/debugprobe-compare-renderer.js index b822ea2..a76edf7 100644 --- a/DebugProbe.AspNetCore/Assets/js/debugprobe-compare-renderer.js +++ b/DebugProbe.AspNetCore/Assets/js/debugprobe-compare-renderer.js @@ -182,7 +182,6 @@ function renderSideBySideJson(comparison, localJson, remoteJson ) {
Local - ${renderPayloadBadge(localJson)}
${renderAlignedJson(comparison.local,localJson)} @@ -191,7 +190,6 @@ function renderSideBySideJson(comparison, localJson, remoteJson ) {
Remote - ${renderPayloadBadge(remoteJson)}
${renderAlignedJson(comparison.remote, remoteJson )} @@ -201,49 +199,6 @@ function renderSideBySideJson(comparison, localJson, remoteJson ) { `; } -function renderPayloadBadge(value) { - - const payloadType = getPayloadType(value); - - return `${payloadType.label}`; -} - -function getPayloadType(value) { - - if (!value || !value.trim()) { - return { - label: 'Empty', - className: 'payload-empty' - }; - } - - try { - JSON.parse(value); - - return { - label: 'JSON', - className: 'payload-json' - }; - } catch { - return looksLikeJson(value) - ? { - label: 'Invalid JSON', - className: 'payload-invalid-json' - } - : { - label: 'Plain Text', - className: 'payload-text' - }; - } -} - -function looksLikeJson(value) { - - const trimmed = value.trimStart(); - - return trimmed.startsWith('{') || trimmed.startsWith('['); -} - function renderAccordionSection(title, content, expanded = false, changes = 0) { return `
@@ -255,9 +210,6 @@ function renderAccordionSection(title, content, expanded = false, changes = 0) {
- - ${changes > 0 ? `${changes}` : ''} - ${expanded ? '-' : '+'} diff --git a/DebugProbe.AspNetCore/Internal/Rendering/HtmlRenderer.cs b/DebugProbe.AspNetCore/Internal/Rendering/HtmlRenderer.cs index 46a9c00..10f5e18 100644 --- a/DebugProbe.AspNetCore/Internal/Rendering/HtmlRenderer.cs +++ b/DebugProbe.AspNetCore/Internal/Rendering/HtmlRenderer.cs @@ -60,6 +60,7 @@ public static string RenderDetailsPage(DebugEntry x, DebugEnvironment e, string .Replace("{{path}}", Encode(pathWithQuery)) .Replace("{{status}}", GetStatusText(x.StatusCode)) .Replace("{{statusClass}}", statusClass) + .Replace("{{responseGroupClass}}", GetResponseGroupClass(x.StatusCode)) .Replace("{{responseStatusCode}}", x.StatusCode.ToString()) .Replace("{{traceId}}", x.Id.ToString()) @@ -80,13 +81,9 @@ public static string RenderDetailsPage(DebugEntry x, DebugEnvironment e, string .Replace("{{assemblyVersion}}", Encode(e.AssemblyVersion)) .Replace("{{requestUrl}}", Encode(string.IsNullOrEmpty(x.RequestUrl) ? "" : x.RequestUrl)) - .Replace("{{requestType}}", GetPayloadType(req)) - .Replace("{{requestTypeClass}}", GetPayloadTypeClass(req)) .Replace("{{requestHeaders}}", Encode(requestHeaders)) .Replace("{{request}}", Encode(string.IsNullOrEmpty(req) ? "" : req)) - .Replace("{{responseType}}", GetPayloadType(res)) - .Replace("{{responseTypeClass}}", GetPayloadTypeClass(res)) .Replace("{{responseHeaders}}", Encode(responseHeaders)) .Replace("{{response}}", Encode(string.IsNullOrEmpty(res) ? "" : res)); @@ -117,56 +114,9 @@ private static string GetStatusClass(int statusCode) }; } - private static string GetPayloadType(string value) + private static string GetResponseGroupClass(int statusCode) { - if (IsCapturePlaceholder(value)) - { - return string.Empty; - } - - if (string.IsNullOrWhiteSpace(value)) - { - return "Empty"; - } - - if (JsonUtils.IsValidJson(value)) - { - return "JSON"; - } - - return LooksLikeJson(value) ? "Invalid JSON" : "Plain Text"; - } - - private static string GetPayloadTypeClass(string value) - { - if (IsCapturePlaceholder(value)) - { - return "payload-hidden"; - } - - if (string.IsNullOrWhiteSpace(value)) - { - return "payload-empty"; - } - - if (JsonUtils.IsValidJson(value)) - { - return "payload-json"; - } - - return LooksLikeJson(value) ? "payload-invalid-json" : "payload-text"; - } - - private static bool IsCapturePlaceholder(string value) - { - return string.Equals(value, "[Body too large]", StringComparison.OrdinalIgnoreCase); - } - - private static bool LooksLikeJson(string value) - { - var trimmed = value.TrimStart(); - - return trimmed.StartsWith('{') || trimmed.StartsWith('['); + return statusCode >= 400 ? "response-error" : ""; } } diff --git a/DebugProbe.AspNetCore/Storage/DebugEntryStore.cs b/DebugProbe.AspNetCore/Storage/DebugEntryStore.cs index a72715b..51e36e8 100644 --- a/DebugProbe.AspNetCore/Storage/DebugEntryStore.cs +++ b/DebugProbe.AspNetCore/Storage/DebugEntryStore.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Reflection; using DebugProbe.AspNetCore.Internal.Utils; +using DebugProbe.AspNetCore.Middleware; using DebugProbe.AspNetCore.Models; using DebugProbe.AspNetCore.Options;