Skip to content

Commit ec67c7a

Browse files
feat(compare): add open in new tab action
1 parent ace902d commit ec67c7a

6 files changed

Lines changed: 141 additions & 2 deletions

File tree

DebugProbe.AspNetCore/Assets/css/debugprobe.css

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,45 @@ h4 {
365365
margin-top: 22px;
366366
}
367367

368+
.compare-page .compare-card {
369+
margin-top: 0;
370+
}
371+
372+
.compare-open-tab {
373+
display: inline-flex;
374+
align-items: center;
375+
justify-content: center;
376+
flex: 0 0 30px;
377+
width: 30px;
378+
height: 30px;
379+
padding: 0;
380+
background: #fff;
381+
border: 1px solid #e5e7eb;
382+
border-radius: 6px;
383+
color: #6b7280;
384+
cursor: pointer;
385+
}
386+
387+
.compare-open-tab:hover {
388+
background: #f9fafb;
389+
border-color: #d1d5db;
390+
color: #111827;
391+
}
392+
393+
.compare-open-tab[hidden] {
394+
display: none;
395+
}
396+
397+
.compare-open-tab svg {
398+
width: 15px;
399+
height: 15px;
400+
stroke: currentColor;
401+
stroke-width: 2;
402+
stroke-linecap: round;
403+
stroke-linejoin: round;
404+
fill: none;
405+
}
406+
368407
.mono-value {
369408
overflow: hidden;
370409
max-width: min(520px, 58vw);

DebugProbe.AspNetCore/Assets/html/details.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,19 @@
106106
<span class="trace-dot" aria-hidden="true"></span>
107107
<span class="trace-label">Compare Trace</span>
108108
</div>
109+
<button id="openCompareTab"
110+
class="compare-open-tab"
111+
type="button"
112+
title="Open in new tab"
113+
aria-label="Open in new tab"
114+
onclick="openCompareInNewTab()"
115+
hidden>
116+
<svg viewBox="0 0 24 24" aria-hidden="true">
117+
<path d="M14 3h7v7"></path>
118+
<path d="M10 14 21 3"></path>
119+
<path d="M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"></path>
120+
</svg>
121+
</button>
109122
</div>
110123

111124
<div class="compare-controls">

DebugProbe.AspNetCore/Assets/js/debugprobe-compare-renderer.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
window.runCompare = async function () {
22

3-
const id = window.location.pathname.split('/').pop();
3+
const id = getLocalTraceId();
44
const base = document.getElementById('baseUrl').value.trim();
55
const remoteId = document.getElementById('compareId').value.trim();
66

@@ -10,6 +10,7 @@ window.runCompare = async function () {
1010
}
1111

1212
setCompareResult('<div class="compare-message">Comparing...</div>');
13+
setOpenCompareButtonVisible(false);
1314

1415
try {
1516
const res = await fetch(`/debug/compare/${id}?baseUrl=${encodeURIComponent(base)}&remoteTraceId=${encodeURIComponent(remoteId)}`);
@@ -21,15 +22,62 @@ window.runCompare = async function () {
2122
}
2223

2324
setCompareResult(renderCompare(await res.json()));
25+
setOpenCompareButtonVisible(true);
2426
} catch (error) {
2527
setCompareResult(`<div class="compare-message compare-message-error">${escapeHtml(error.message || 'Compare failed')}</div>`);
2628
}
2729
};
2830

31+
window.openCompareInNewTab = function () {
32+
const localTraceId = getLocalTraceId();
33+
const base = document.getElementById('baseUrl').value.trim();
34+
const traceId = document.getElementById('compareId').value.trim();
35+
36+
if (!base || !traceId) {
37+
return;
38+
}
39+
40+
const url = `/compare?baseUrl=${encodeURIComponent(base)}&traceId=${encodeURIComponent(traceId)}&localTraceId=${encodeURIComponent(localTraceId)}`;
41+
window.open(url, '_blank', 'noopener');
42+
};
43+
44+
document.addEventListener('DOMContentLoaded', () => {
45+
const baseInput = document.getElementById('baseUrl');
46+
const traceInput = document.getElementById('compareId');
47+
48+
if (!baseInput || !traceInput) {
49+
return;
50+
}
51+
52+
const params = new URLSearchParams(window.location.search);
53+
const baseUrl = params.get('baseUrl');
54+
const traceId = params.get('traceId');
55+
56+
if (!baseUrl || !traceId) {
57+
return;
58+
}
59+
60+
baseInput.value = baseUrl;
61+
traceInput.value = traceId;
62+
window.runCompare();
63+
});
64+
2965
function setCompareResult(html) {
3066
document.getElementById('compareResult').innerHTML = html;
3167
}
3268

69+
function getLocalTraceId() {
70+
const input = document.getElementById('localTraceId');
71+
return input?.value || window.location.pathname.split('/').pop();
72+
}
73+
74+
function setOpenCompareButtonVisible(visible) {
75+
const button = document.getElementById('openCompareTab');
76+
if (button) {
77+
button.hidden = !visible;
78+
}
79+
}
80+
3381
function renderCompare(result) {
3482
const model = buildCompareModel(result);
3583

DebugProbe.AspNetCore/Extensions/DebugProbeExtensions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,19 @@ public static IApplicationBuilder UseDebugProbe(this IApplicationBuilder app)
9999

100100
}).ExcludeFromDescription();
101101

102+
webApp.MapGet("/compare", (string? baseUrl, string? traceId, string? localTraceId) =>
103+
{
104+
if (string.IsNullOrWhiteSpace(localTraceId))
105+
{
106+
return Results.BadRequest("Missing local trace id");
107+
}
108+
109+
var html = HtmlRenderer.RenderComparePage(localTraceId, baseUrl ?? "", traceId ?? "");
110+
111+
return Results.Content(html, "text/html");
112+
113+
}).ExcludeFromDescription();
114+
102115
webApp.MapGet("/debug/compare/{id}", async (string id, string baseUrl, string remoteTraceId,
103116
DebugEntryStore store,
104117
DebugProbeOptions options) =>

DebugProbe.AspNetCore/Internal/Rendering/HtmlRenderer.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,31 @@ public static string RenderDetailsPage(DebugEntry x, DebugEnvironment e, string
129129
return BuildLayout(content);
130130
}
131131

132+
public static string RenderComparePage(string localTraceId, string baseUrl, string traceId)
133+
{
134+
var content = $@"
135+
<div class=""container compare-page"">
136+
<section class=""trace-card compare-card"" aria-label=""Compare trace result"">
137+
<div class=""trace-card-main"">
138+
<div class=""trace-card-header"">
139+
<div class=""trace-card-title"">
140+
<span class=""trace-dot"" aria-hidden=""true""></span>
141+
<span class=""trace-label"">Compare Trace</span>
142+
</div>
143+
</div>
144+
<input id=""localTraceId"" type=""hidden"" value=""{Encode(localTraceId)}"" />
145+
<input id=""baseUrl"" type=""hidden"" value=""{Encode(baseUrl)}"" />
146+
<input id=""compareId"" type=""hidden"" value=""{Encode(traceId)}"" />
147+
<div id=""compareResult"">
148+
<div class=""compare-message"">Comparing...</div>
149+
</div>
150+
</div>
151+
</section>
152+
</div>";
153+
154+
return BuildLayout(content);
155+
}
156+
132157
private static string BuildOutgoingRequestCard(DebugOutgoingRequest request)
133158
{
134159
var classes = request.StatusCode >= 400 || !string.IsNullOrWhiteSpace(request.Exception)

DebugProbe.AspNetCore/Middleware/DebugProbeMiddleware.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public class DebugProbeMiddleware
2121
private static readonly string[] DefaultIgnorePaths =
2222
[
2323
"/debug",
24+
"/compare",
2425
"/swagger",
2526
"/.well-known",
2627

@@ -252,4 +253,4 @@ private static async Task<byte[]> ReadAtMostAsync(Stream stream, int byteLimit)
252253

253254
return buffer.ToArray();
254255
}
255-
}
256+
}

0 commit comments

Comments
 (0)