Visual improvements to debug mode for better information visibility
This commit is contained in:
@@ -5,6 +5,7 @@ function createDebugContentView({
|
||||
formatDebugBody,
|
||||
formatHeaderBlock,
|
||||
formatMetadataBlock,
|
||||
createCloseIcon,
|
||||
environment
|
||||
}) {
|
||||
const { document } = environment;
|
||||
@@ -16,6 +17,7 @@ function createDebugContentView({
|
||||
];
|
||||
let selectedCategory = "all";
|
||||
const expandedIds = new Set();
|
||||
let returnFocus = null;
|
||||
|
||||
function getRequestCategory(entry) {
|
||||
if (/^request(?:TokenRelay|ConsumerTokenService|CustomerTokenService)/.test(entry.name)) return "authentication";
|
||||
@@ -45,8 +47,22 @@ function createDebugContentView({
|
||||
return;
|
||||
}
|
||||
|
||||
panel.hidden = !panel.hidden;
|
||||
if (!panel.hidden) {
|
||||
closeDebugPanel();
|
||||
return;
|
||||
}
|
||||
returnFocus = document.activeElement;
|
||||
panel.hidden = false;
|
||||
renderDebugPanel();
|
||||
panel.querySelectorAll(".opportunities-extension-debug-close-button")[0]?.focus({ preventScroll: true });
|
||||
}
|
||||
|
||||
function closeDebugPanel() {
|
||||
const panel = document.getElementById(DEBUG_PANEL_ID);
|
||||
if (!panel) return;
|
||||
panel.hidden = true;
|
||||
if (returnFocus?.isConnected) returnFocus.focus({ preventScroll: true });
|
||||
returnFocus = null;
|
||||
}
|
||||
|
||||
function renderDebugPanel() {
|
||||
@@ -62,14 +78,21 @@ function createDebugContentView({
|
||||
else expandedIds.delete(id);
|
||||
}
|
||||
const focusedCategory = document.activeElement && document.activeElement.getAttribute("data-debug-category");
|
||||
const focusedControl = document.activeElement && document.activeElement.getAttribute("data-debug-focus");
|
||||
const listScrollTop = panel.querySelectorAll(".opportunities-extension-debug-list")[0]?.scrollTop || 0;
|
||||
const scrollTop = panel.scrollTop;
|
||||
panel.textContent = "";
|
||||
panel.setAttribute("role", "dialog");
|
||||
panel.setAttribute("aria-modal", "true");
|
||||
panel.setAttribute("aria-labelledby", `${DEBUG_PANEL_ID}-title`);
|
||||
const focusTargets = new Map();
|
||||
|
||||
const panelHeader = document.createElement("header");
|
||||
panelHeader.className = "opportunities-extension-debug-panel-header";
|
||||
|
||||
const heading = document.createElement("div");
|
||||
const title = document.createElement("h2");
|
||||
title.id = `${DEBUG_PANEL_ID}-title`;
|
||||
title.textContent = "Debug Requests";
|
||||
|
||||
const summary = document.createElement("p");
|
||||
@@ -81,6 +104,8 @@ function createDebugContentView({
|
||||
const visibilityButton = document.createElement("button");
|
||||
visibilityButton.type = "button";
|
||||
visibilityButton.className = "opportunities-extension-icon-button opportunities-extension-debug-visibility-button";
|
||||
visibilityButton.setAttribute("data-debug-focus", "visibility");
|
||||
focusTargets.set("visibility", visibilityButton);
|
||||
visibilityButton.setAttribute("aria-pressed", String(debugState.debugSecretsVisible));
|
||||
visibilityButton.setAttribute("aria-label", debugState.debugSecretsVisible ? "Hide sensitive values" : "Show sensitive values");
|
||||
visibilityButton.title = debugState.debugSecretsVisible ? "Hide sensitive values" : "Show sensitive values";
|
||||
@@ -89,7 +114,19 @@ function createDebugContentView({
|
||||
debugState.debugSecretsVisible = !debugState.debugSecretsVisible;
|
||||
renderDebugPanel();
|
||||
});
|
||||
panelHeader.append(heading, visibilityButton);
|
||||
const closeButton = document.createElement("button");
|
||||
closeButton.type = "button";
|
||||
closeButton.className = "opportunities-extension-icon-button opportunities-extension-debug-close-button";
|
||||
closeButton.title = "Close debug";
|
||||
closeButton.setAttribute("aria-label", "Close debug");
|
||||
closeButton.setAttribute("data-debug-focus", "close");
|
||||
closeButton.append(createCloseIcon());
|
||||
closeButton.addEventListener("click", closeDebugPanel);
|
||||
focusTargets.set("close", closeButton);
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "opportunities-extension-debug-actions";
|
||||
actions.append(visibilityButton, closeButton);
|
||||
panelHeader.append(heading, actions);
|
||||
|
||||
const entries = getChronologicalRequests();
|
||||
const availableCategories = categories.filter(([key]) => key === "all" || entries.some(entry => getRequestCategory(entry) === key));
|
||||
@@ -136,6 +173,9 @@ function createDebugContentView({
|
||||
list.setAttribute("role", "tabpanel");
|
||||
list.setAttribute("aria-labelledby", `${DEBUG_PANEL_ID}-tab-${selectedCategory}`);
|
||||
list.tabIndex = 0;
|
||||
list.setAttribute("data-debug-focus", "list");
|
||||
focusTargets.set("list", list);
|
||||
const summaries = [];
|
||||
|
||||
if (debugState.requestLog.length === 0) {
|
||||
const empty = document.createElement("div");
|
||||
@@ -152,6 +192,9 @@ function createDebugContentView({
|
||||
|
||||
const heading = document.createElement("summary");
|
||||
heading.className = "opportunities-extension-debug-heading";
|
||||
heading.setAttribute("data-debug-focus", `request-${entry.id}`);
|
||||
focusTargets.set(`request-${entry.id}`, heading);
|
||||
summaries.push(heading);
|
||||
|
||||
const name = document.createElement("strong");
|
||||
name.textContent = `${entry.id}. ${entry.name}`;
|
||||
@@ -162,6 +205,7 @@ function createDebugContentView({
|
||||
const httpStatus = document.createElement("span");
|
||||
httpStatus.className = "opportunities-extension-debug-http-status";
|
||||
httpStatus.textContent = entry.httpStatus ? String(entry.httpStatus) : "pending";
|
||||
httpStatus.setAttribute("data-http-class", entry.httpStatus ? String(Math.floor(entry.httpStatus / 100)) : "pending");
|
||||
|
||||
const status = document.createElement("span");
|
||||
status.className = "opportunities-extension-debug-status";
|
||||
@@ -173,7 +217,11 @@ function createDebugContentView({
|
||||
startedTime.className = "opportunities-extension-debug-time";
|
||||
startedTime.textContent = formatDebugTime(entry.startedAt);
|
||||
startedTime.title = `Started: ${entry.startedAt.toLocaleString()}`;
|
||||
summaryMeta.append(startedTime, httpStatus, status);
|
||||
const method = document.createElement("span");
|
||||
method.className = "opportunities-extension-debug-method";
|
||||
method.textContent = entry.method || "GET";
|
||||
method.setAttribute("data-method", method.textContent.toUpperCase());
|
||||
summaryMeta.append(startedTime, method, httpStatus, status);
|
||||
|
||||
heading.append(name, summaryMeta);
|
||||
|
||||
@@ -200,7 +248,23 @@ function createDebugContentView({
|
||||
});
|
||||
|
||||
panel.append(panelHeader, tabs, list);
|
||||
panel.onkeydown = event => {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
closeDebugPanel();
|
||||
} else if (event.key === "Tab") {
|
||||
const controls = [visibilityButton, closeButton, ...tabButtons.filter(button => button.tabIndex === 0), list, ...summaries];
|
||||
const index = controls.indexOf(document.activeElement);
|
||||
if (index < 0 || (event.shiftKey && index === 0) || (!event.shiftKey && index === controls.length - 1)) {
|
||||
event.preventDefault();
|
||||
controls[event.shiftKey ? controls.length - 1 : 0].focus({ preventScroll: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
if (focusedCategory) tabButtons.find(button => button.getAttribute("data-debug-category") === selectedCategory).focus({ preventScroll: true });
|
||||
else if (focusedControl) focusTargets.get(focusedControl)?.focus({ preventScroll: true });
|
||||
list.scrollTop = listScrollTop;
|
||||
panel.scrollTop = scrollTop;
|
||||
}
|
||||
|
||||
@@ -240,15 +304,41 @@ function createDebugContentView({
|
||||
|
||||
function appendDebugBlock(container, label, value) {
|
||||
const block = document.createElement("section");
|
||||
block.className = "opportunities-extension-debug-block";
|
||||
block.setAttribute("data-direction", label.startsWith("Response") ? "response" : "request");
|
||||
const heading = document.createElement("h3");
|
||||
const content = document.createElement("pre");
|
||||
|
||||
heading.textContent = label;
|
||||
content.textContent = value;
|
||||
appendJsonTokens(content, value);
|
||||
block.append(heading, content);
|
||||
container.append(block);
|
||||
}
|
||||
|
||||
function appendJsonTokens(content, value) {
|
||||
const text = String(value ?? "");
|
||||
try { JSON.parse(text); } catch { content.textContent = text; return; }
|
||||
// Highlight only already-masked display text; never interpret response content as HTML.
|
||||
const tokens = [...text.matchAll(/"(?:[^"\\]|\\.)*"\s*:|"(?:[^"\\]|\\.)*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|\b(?:true|false|null)\b/g)];
|
||||
let offset = 0;
|
||||
const append = (textValue, kind) => {
|
||||
if (!textValue) return;
|
||||
const span = document.createElement("span");
|
||||
span.textContent = textValue;
|
||||
if (kind) span.className = `opportunities-extension-debug-json-${kind}`;
|
||||
content.append(span);
|
||||
};
|
||||
tokens.forEach(token => {
|
||||
append(text.slice(offset, token.index));
|
||||
const value = token[0];
|
||||
const kind = value.startsWith('"') ? value.endsWith(":") ? "key" : "string"
|
||||
: /^(true|false|null)$/.test(value) ? "literal" : "number";
|
||||
append(value, kind);
|
||||
offset = token.index + value.length;
|
||||
});
|
||||
append(text.slice(offset));
|
||||
}
|
||||
|
||||
function formatDebugTime(date) {
|
||||
return date.toLocaleTimeString(undefined, {
|
||||
hour: "2-digit",
|
||||
@@ -257,5 +347,5 @@ function createDebugContentView({
|
||||
});
|
||||
}
|
||||
|
||||
return { toggleDebugPanel, renderDebugPanel, createDebugVisibilityIcon, appendDebugBlock, formatDebugTime, getRequestCategory, getChronologicalRequests };
|
||||
return { toggleDebugPanel, closeDebugPanel, renderDebugPanel, createDebugVisibilityIcon, appendDebugBlock, formatDebugTime, getRequestCategory, getChronologicalRequests };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user