feat(options): add diagnostics copy action

This commit is contained in:
2026-08-02 22:23:08 -04:00
parent d55006dd0a
commit b4234e0387
5 changed files with 137 additions and 48 deletions
+1
View File
@@ -1029,6 +1029,7 @@
<button id="restore">Restore Defaults</button>
<button id="exportSettings">Export Settings</button>
<button id="importSettings">Import Settings</button>
<button id="copyDiagnostics">Copy Diagnostics</button>
</div>
<div id="status" role="status" aria-live="polite"></div>
+35
View File
@@ -2104,6 +2104,41 @@ document.addEventListener("DOMContentLoaded", function () {
.addEventListener("change", updatePopupEditorDisabledState);
document.getElementById("save").addEventListener("click", save_options);
document.getElementById("copyDiagnostics").addEventListener("click", function () {
var status = document.getElementById("status");
if (
!navigator.clipboard ||
typeof navigator.clipboard.writeText !== "function"
) {
status.textContent = "Clipboard access is unavailable.";
return;
}
var report = popupControlUtils.buildDiagnosticReport({
speederVersion: manifest.version,
browser: navigator.userAgent,
platform: navigator.platform || null,
storage: {
enabled: document.getElementById("enabled").checked,
rememberSpeed: document.getElementById("rememberSpeed").checked,
forceLastSavedSpeed:
document.getElementById("forceLastSavedSpeed").checked,
audioBoolean: document.getElementById("audioBoolean").checked,
startHidden: document.getElementById("startHidden").checked,
hideWithControls: document.getElementById("hideWithControls").checked,
controllerLocation: document.getElementById("controllerLocation").value,
shortcutTargetMode:
document.getElementById("shortcutTargetMode").value
}
});
navigator.clipboard.writeText(report).then(
function() {
status.textContent = "Diagnostics copied. Review before sharing.";
},
function() {
status.textContent = "Could not copy diagnostics.";
}
);
});
const addSelector = document.getElementById("addShortcutSelector");
if (addSelector) {
+11 -48
View File
@@ -54,18 +54,6 @@ document.addEventListener("DOMContentLoaded", function () {
);
}
function getSafePageDetails(url) {
try {
var parsed = new URL(url);
return {
protocol: parsed.protocol,
hostname: parsed.hostname || null
};
} catch (_error) {
return { protocol: null, hostname: null };
}
}
function getCurrentSiteRuleDetails(url) {
try {
var parsed = new URL(url);
@@ -86,42 +74,17 @@ document.addEventListener("DOMContentLoaded", function () {
function buildDiagnosticReport() {
if (!diagnosticContext) return null;
var storage = diagnosticContext.storage;
var siteRule = diagnosticContext.siteRule;
var frame = diagnosticContext.frame;
return JSON.stringify(
{
speederVersion: chrome.runtime.getManifest().version,
browser: navigator.userAgent,
platform: navigator.platform || null,
page: getSafePageDetails(diagnosticContext.url),
globalSettings: {
enabled: storage.enabled !== false,
rememberSpeed: storage.rememberSpeed === true,
forceLastSavedSpeed: storage.forceLastSavedSpeed === true,
audioEnabled: storage.audioBoolean === true,
startHidden: storage.startHidden === true,
hideWithControls: storage.hideWithControls === true,
controllerLocation: storage.controllerLocation,
shortcutTargetMode: storage.shortcutTargetMode
},
matchedSiteRule: {
matched: Boolean(siteRule),
disabled: isSiteRuleDisabled(siteRule),
overrideKeys: siteRule
? Object.keys(siteRule)
.filter(function(key) {
return key !== "pattern" && key !== "title";
})
.sort()
: []
},
tabPaused: diagnosticContext.tabPaused === true,
frame: frame && frame.diagnostics ? frame.diagnostics : null
},
null,
2
);
return popupControlUtils.buildDiagnosticReport({
speederVersion: chrome.runtime.getManifest().version,
browser: navigator.userAgent,
platform: navigator.platform || null,
url: diagnosticContext.url,
storage: diagnosticContext.storage,
siteRule: diagnosticContext.siteRule,
siteRuleDisabled: isSiteRuleDisabled(diagnosticContext.siteRule),
tabPaused: diagnosticContext.tabPaused,
frame: diagnosticContext.frame
});
}
function persistExpandedSettings(rawStorage, settings, callback) {
+54
View File
@@ -97,7 +97,61 @@
return fallback;
}
function getSafePageDetails(url) {
try {
var parsed = new URL(url);
return {
protocol: parsed.protocol,
hostname: parsed.hostname || null
};
} catch (_error) {
return { protocol: null, hostname: null };
}
}
function buildDiagnosticReport(context) {
var config = context || {};
var storage = config.storage || {};
var siteRule = config.siteRule || null;
var frame = config.frame || null;
return JSON.stringify(
{
speederVersion: config.speederVersion || null,
browser: config.browser || null,
platform: config.platform || null,
page: getSafePageDetails(config.url),
globalSettings: {
enabled: storage.enabled !== false,
rememberSpeed: storage.rememberSpeed === true,
forceLastSavedSpeed: storage.forceLastSavedSpeed === true,
audioEnabled: storage.audioBoolean === true,
startHidden: storage.startHidden === true,
hideWithControls: storage.hideWithControls === true,
controllerLocation: storage.controllerLocation,
shortcutTargetMode: storage.shortcutTargetMode
},
matchedSiteRule: {
matched: Boolean(siteRule),
disabled: config.siteRuleDisabled === true,
overrideKeys: siteRule
? Object.keys(siteRule)
.filter(function(key) {
return key !== "pattern" && key !== "title";
})
.sort()
: []
},
tabPaused: config.tabPaused === true,
frame: frame && frame.diagnostics ? frame.diagnostics : null
},
null,
2
);
}
return {
buildDiagnosticReport: buildDiagnosticReport,
pickBestFrameSpeedResult: pickBestFrameSpeedResult,
resolvePopupButtons: resolvePopupButtons,
sanitizeButtonOrder: sanitizeButtonOrder
+36
View File
@@ -84,6 +84,42 @@ describe("options.js", () => {
expect(document.getElementById("status").textContent).toBe("Auto-saved");
});
it("copies privacy-safe diagnostics from settings", async () => {
bootOptions({
syncData: {
enabled: false,
rememberSpeed: true,
siteRules: [
{
title: "Private account",
pattern: "secret.example/private-token",
enabled: true
}
]
}
});
const writeText = vi.fn(() => Promise.resolve());
Object.defineProperty(navigator, "clipboard", {
configurable: true,
value: { writeText }
});
await flushAsyncWork(3);
document.getElementById("copyDiagnostics").click();
await flushAsyncWork(3);
const reportText = writeText.mock.calls[0][0];
const report = JSON.parse(reportText);
expect(report.globalSettings.enabled).toBe(false);
expect(report.globalSettings.rememberSpeed).toBe(true);
expect(report.page).toEqual({ protocol: null, hostname: null });
expect(report.matchedSiteRule.matched).toBe(false);
expect(report.frame).toBeNull();
expect(reportText).not.toContain("Private account");
expect(reportText).not.toContain("private-token");
expect(document.getElementById("status").textContent).toContain("copied");
});
it("does not partially save options when a required site shortcut is invalid", async () => {
const chrome = bootOptions({ syncData: { rememberSpeed: false } });
await flushAsyncWork(3);