Step 6c: extract Diagnostics + Profiles + Rules Editors from options.js
Final options.js split. Three new files:
options-diagnostics.js 245 lines
options-profiles.js 265 lines
options-rules-editors.js 328 lines (adapters + ID normalizers
+ custom part detectors)
options.js: 1852 → 1014 lines (838 extracted, ~45% reduction).
Script-tag order in options.html now (load order matters for
top-level let bindings shared across files, e.g. _configuredScanRoots):
options-cache.js
options-dupe-review.js
options-library-issues.js
options-diagnostics.js
options-profiles.js
options-rules-editors.js
options.js (entry: IIFE bottom, escapeHtml, overlay previews,
element picker, paths)
The picker, overlay-preview, and no-match overlay code stays in
options.js — those are tightly intertwined with multiple settings
panes and not worth further splitting today.
node --check passes on each file individually and on the concatenated
load-order stream. Line count of concat (3144) matches the pre-split
sum exactly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
// ---------- diagnostics ----------
|
||||
|
||||
// Extension ID display + copy button (added when Transfer Assistant was deleted).
|
||||
// Diagnostics is the canonical home for "what's my extension ID?" info now.
|
||||
(() => {
|
||||
const idEl = document.getElementById("diag-extension-id");
|
||||
const copyBtn = document.getElementById("diag-copy-extension-id");
|
||||
if (idEl) idEl.textContent = chrome.runtime.id;
|
||||
if (copyBtn) {
|
||||
copyBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(chrome.runtime.id);
|
||||
copyBtn.textContent = "Copied";
|
||||
setTimeout(() => { copyBtn.textContent = "Copy ID"; }, 1200);
|
||||
} catch (_) {
|
||||
copyBtn.textContent = "Copy failed";
|
||||
setTimeout(() => { copyBtn.textContent = "Copy ID"; }, 1200);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
document.getElementById("run-diag").addEventListener("click", (event) =>
|
||||
keepActionViewport(event.currentTarget, runDiagnostics)
|
||||
);
|
||||
document.getElementById("host-status-run").addEventListener("click", (event) =>
|
||||
keepActionViewport(event.currentTarget, runHostStatus)
|
||||
);
|
||||
document.getElementById("host-repair-run").addEventListener("click", (event) =>
|
||||
keepActionViewport(event.currentTarget, runHostRepair)
|
||||
);
|
||||
document.getElementById("host-verify-run").addEventListener("click", (event) =>
|
||||
keepActionViewport(event.currentTarget, runHostStatus)
|
||||
);
|
||||
document.getElementById("run-all-diag").addEventListener("click", (event) =>
|
||||
keepActionViewport(event.currentTarget, async () => {
|
||||
clearNativeRepairCard();
|
||||
const runtime = await runDiagnostics();
|
||||
if (runtime && runtime.nativeBlocked) {
|
||||
renderBlockedByNativeIssue(document.getElementById("host-status-results"), "Host registration");
|
||||
return;
|
||||
}
|
||||
await runHostStatus();
|
||||
})
|
||||
);
|
||||
|
||||
function renderDiagRows(out, checks, emptyLabel) {
|
||||
out.innerHTML = "";
|
||||
if (!checks || checks.length === 0) {
|
||||
out.innerHTML = `<div class="diag-row warn"><span class="icon">!</span><span class="name">${escapeHtml(emptyLabel)}</span><span class="detail">no checks returned</span></div>`;
|
||||
return;
|
||||
}
|
||||
const counts = checks.reduce((acc, c) => {
|
||||
const status = c.status || "warn";
|
||||
acc[status] = (acc[status] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
const summary = document.createElement("div");
|
||||
summary.className = "diag-row " + ((counts.fail || 0) ? "fail" : (counts.warn || 0) ? "warn" : "ok");
|
||||
summary.innerHTML = `<span class="icon">#</span><span class="name">summary</span><span class="detail">${checks.length} checks · ok ${counts.ok || 0} · info ${counts.info || 0} · warn ${counts.warn || 0} · fail ${counts.fail || 0}</span>`;
|
||||
out.appendChild(summary);
|
||||
for (const c of checks) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "diag-row " + (c.status || "warn");
|
||||
const status = c.status || "warn";
|
||||
const icon = status === "ok" ? "✓" : status === "info" ? "i" : status === "warn" ? "!" : "✗";
|
||||
row.innerHTML = `<span class="icon">${icon}</span><span class="name">${escapeHtml(c.name)}</span><span class="detail">${formatDiagDetail(c.detail || "")}</span>`;
|
||||
out.appendChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
function formatDiagDetail(detail) {
|
||||
const text = String(detail || "");
|
||||
if (!text) return "";
|
||||
const shouldCollapse = text.length > 120 || text.includes("\n") || (text.match(/[;|]/g) || []).length > 2;
|
||||
if (!shouldCollapse) return escapeHtml(text);
|
||||
const first = text.split(/\r?\n/)[0].slice(0, 110);
|
||||
return `<details><summary>${escapeHtml(first)}${text.length > first.length ? "…" : ""}</summary><pre>${escapeHtml(text)}</pre></details>`;
|
||||
}
|
||||
|
||||
async function runDiagnostics() {
|
||||
const out = document.getElementById("diag-results");
|
||||
clearNativeRepairCard();
|
||||
out.innerHTML = '<div class="diag-row warn"><span class="icon">…</span><span class="name">running…</span><span class="detail">waiting for native host</span></div>';
|
||||
try {
|
||||
const r = await chrome.runtime.sendMessage({ type: "diagnostics" });
|
||||
if (!r || !r.ok) {
|
||||
await renderNativeMessagingFailure(r);
|
||||
renderBlockedByNativeIssue(out, "Runtime diagnostics");
|
||||
return { nativeBlocked: true };
|
||||
}
|
||||
renderDiagRows(out, r.checks || [], "runtime");
|
||||
return { ok: true };
|
||||
} catch (err) {
|
||||
out.innerHTML = `<div class="diag-row fail"><span class="icon">✗</span><span class="name">runtime</span><span class="detail">${escapeHtml(err.message || String(err))}</span></div>`;
|
||||
return { ok: false };
|
||||
}
|
||||
}
|
||||
|
||||
async function runHostStatus() {
|
||||
const out = document.getElementById("host-status-results");
|
||||
clearNativeRepairCard();
|
||||
out.innerHTML = '<div class="diag-row warn"><span class="icon">…</span><span class="name">checking…</span><span class="detail">reading manifest and registry state</span></div>';
|
||||
try {
|
||||
const r = await chrome.runtime.sendMessage({ type: "host-status" });
|
||||
if (!r || !r.ok) {
|
||||
await renderNativeMessagingFailure(r);
|
||||
renderBlockedByNativeIssue(out, "Native host checks");
|
||||
return { nativeBlocked: true };
|
||||
}
|
||||
renderDiagRows(out, r.checks || [], "host status");
|
||||
return { ok: true };
|
||||
} catch (err) {
|
||||
out.innerHTML = `<div class="diag-row fail"><span class="icon">✗</span><span class="name">host status</span><span class="detail">${escapeHtml(err.message || String(err))}</span></div>`;
|
||||
return { ok: false };
|
||||
}
|
||||
}
|
||||
|
||||
async function runHostRepair() {
|
||||
const out = document.getElementById("host-status-results");
|
||||
clearNativeRepairCard();
|
||||
out.innerHTML = '<div class="diag-row warn"><span class="icon">…</span><span class="name">repairing…</span><span class="detail">updating reachable native host manifest and user registration</span></div>';
|
||||
try {
|
||||
const r = await chrome.runtime.sendMessage({ type: "repair-host" });
|
||||
if (!r || !r.ok) {
|
||||
if (r?.error_kind) {
|
||||
await renderNativeMessagingFailure(r);
|
||||
renderBlockedByNativeIssue(out, "Registration repair");
|
||||
} else {
|
||||
out.innerHTML = `<div class="diag-row fail"><span class="icon">✗</span><span class="name">Registration repair</span><span class="detail">${escapeHtml(r?.error || "repair failed")}</span></div>`;
|
||||
}
|
||||
return { ok: false };
|
||||
}
|
||||
const checks = r.verification?.checks || [];
|
||||
renderDiagRows(out, checks, "repair verification");
|
||||
renderCompletedNativeRepair(r);
|
||||
return { ok: true };
|
||||
} catch (err) {
|
||||
out.innerHTML = `<div class="diag-row fail"><span class="icon">✗</span><span class="name">Registration repair</span><span class="detail">${escapeHtml(err.message || String(err))}</span></div>`;
|
||||
return { ok: false };
|
||||
}
|
||||
}
|
||||
|
||||
function clearNativeRepairCard() {
|
||||
const card = document.getElementById("native-repair-card");
|
||||
const out = document.getElementById("native-repair-results");
|
||||
const title = document.getElementById("native-repair-title");
|
||||
if (card) card.style.display = "none";
|
||||
if (out) out.innerHTML = "";
|
||||
if (title) title.textContent = "Native host setup";
|
||||
}
|
||||
|
||||
function renderCompletedNativeRepair(response) {
|
||||
const card = document.getElementById("native-repair-card");
|
||||
const out = document.getElementById("native-repair-results");
|
||||
if (!card || !out) return;
|
||||
card.style.display = "";
|
||||
const title = document.getElementById("native-repair-title");
|
||||
if (title) title.textContent = "Registration repair completed";
|
||||
const regs = (response.registrations || []).filter((x) => x.status === "ok").length;
|
||||
out.innerHTML = `
|
||||
<div class="diag-row ok"><span class="icon">✓</span><span class="name">Repair applied</span><span class="detail">${escapeHtml(response.message || "native host registration repaired")}</span></div>
|
||||
<div class="diag-row info"><span class="icon">i</span><span class="name">Manifest</span><span class="detail">${escapeHtml(response.manifest_path || "")}</span></div>
|
||||
<div class="diag-row info"><span class="icon">i</span><span class="name">User registry</span><span class="detail">${escapeHtml(`${regs} HKCU registration entr${regs === 1 ? "y" : "ies"} updated`)}</span></div>
|
||||
<div class="diag-row warn"><span class="icon">!</span><span class="name">Restart required</span><span class="detail">Fully close Brave, reopen it, reload the extension, then click Verify Registration. If Brave still blocks the host, run the registration steps shown by Diagnostics.</span></div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderBlockedByNativeIssue(out, title) {
|
||||
out.innerHTML = `<div class="diag-row info"><span class="icon">i</span><span class="name">${escapeHtml(title)}</span><span class="detail">Blocked until this PC registers the native host for the current extension ID. Use the setup card above.</span></div>`;
|
||||
}
|
||||
|
||||
async function getPackagedHostPaths() {
|
||||
try {
|
||||
const resp = await fetch(chrome.runtime.getURL("host/com.rcjav.host.json"));
|
||||
if (!resp.ok) return {};
|
||||
const manifest = await resp.json();
|
||||
const bat = manifest.path || "";
|
||||
const hostDir = bat.replace(/[\\/][^\\/]+$/, "");
|
||||
return {
|
||||
hostBat: bat,
|
||||
hostDir,
|
||||
registerBat: hostDir ? hostDir + "\\register-host.bat" : "",
|
||||
installPs1: hostDir ? hostDir + "\\install-host.ps1" : "",
|
||||
};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async function renderNativeMessagingFailure(response) {
|
||||
const card = document.getElementById("native-repair-card");
|
||||
const out = document.getElementById("native-repair-results");
|
||||
if (!card || !out) return;
|
||||
card.style.display = "";
|
||||
const title = document.getElementById("native-repair-title");
|
||||
if (title) title.textContent = "Register host on this PC";
|
||||
const error = response?.error || "no response";
|
||||
const kind = response?.error_kind || (/forbidden/i.test(error) ? "forbidden" : "unknown");
|
||||
const extensionId = response?.extension_id || chrome.runtime.id;
|
||||
const paths = await getPackagedHostPaths();
|
||||
const installCommand = paths.installPs1
|
||||
? `pwsh -ExecutionPolicy Bypass -File "${paths.installPs1}" -ExtensionId ${extensionId}`
|
||||
: `pwsh -ExecutionPolicy Bypass -File ".\\host\\install-host.ps1" -ExtensionId ${extensionId}`;
|
||||
const registerCommand = paths.registerBat ? `"${paths.registerBat}"` : ".\\host\\register-host.bat";
|
||||
let cause = "This extension cannot launch the native messaging host yet.";
|
||||
let fix = "Register the host for this extension ID, fully restart Brave, then verify registration.";
|
||||
if (kind === "forbidden") {
|
||||
cause = "Brave found the native host, but this extension ID is not allowed to launch it on this PC.";
|
||||
fix = "This usually happens after loading the extension on another PC or under a different extension ID.";
|
||||
} else if (kind === "not_found") {
|
||||
cause = "Brave could not find a registered native messaging host for com.rcjav.host on this PC.";
|
||||
fix = "Run the registration script from the extension host folder.";
|
||||
} else if (kind === "disconnected") {
|
||||
cause = "The native host started and then disconnected or crashed.";
|
||||
fix = "After registration is fixed, run Runtime diagnostics again to check Python, rc-jav, and rclone.";
|
||||
} else if (kind === "timeout") {
|
||||
cause = "The native host did not respond before the timeout.";
|
||||
fix = "Restart Brave and check whether a scan or rclone command is stuck.";
|
||||
}
|
||||
out.innerHTML = `
|
||||
<div class="diag-row warn"><span class="icon">!</span><span class="name">Setup required</span><span class="detail">Native host registration must be fixed before cache, runtime, and host checks can run.</span></div>
|
||||
<div class="diag-row warn"><span class="icon">!</span><span class="name">Likely cause</span><span class="detail">${escapeHtml(cause)}</span></div>
|
||||
<div class="diag-row info"><span class="icon">i</span><span class="name">Host message</span><span class="detail">${escapeHtml(error)}</span></div>
|
||||
<div class="diag-row ok"><span class="icon">→</span><span class="name">Fix on this PC</span><span class="detail">${escapeHtml(fix)}</span></div>
|
||||
<div class="diag-row info"><span class="icon">i</span><span class="name">Extension ID</span><span class="detail">${escapeHtml(extensionId)}</span></div>
|
||||
<div class="diag-row info"><span class="icon">1</span><span class="name">Run register-host</span><span class="detail">
|
||||
<details open><summary>${escapeHtml(registerCommand)}</summary><pre>${escapeHtml(`Run ${registerCommand}\nWhen it asks for the extension ID, enter:\n${extensionId}\n\nPowerShell alternative:\n${installCommand}`)}</pre></details>
|
||||
<span class="diag-action"><button type="button" data-copy="${escapeHtml(registerCommand)}" data-copy-label="Copy Script Path">Copy Script Path</button><button type="button" data-copy="${escapeHtml(extensionId)}" data-copy-label="Copy Extension ID">Copy Extension ID</button><button type="button" data-copy="${escapeHtml(installCommand)}" data-copy-label="Copy PowerShell Alternative">Copy PowerShell Alternative</button></span>
|
||||
</span></div>
|
||||
<div class="diag-row info"><span class="icon">2</span><span class="name">Restart Brave</span><span class="detail">Close every Brave window/process, reopen Brave, then reload the extension.</span></div>
|
||||
<div class="diag-row info"><span class="icon">3</span><span class="name">Verify</span><span class="detail"><span class="diag-action"><button type="button" data-verify-registration>Verify Registration</button></span></span></div>
|
||||
`;
|
||||
for (const btn of out.querySelectorAll("button[data-copy]")) {
|
||||
btn.addEventListener("click", async () => {
|
||||
await navigator.clipboard.writeText(btn.dataset.copy || "");
|
||||
btn.textContent = "Copied";
|
||||
setTimeout(() => { btn.textContent = btn.dataset.copyLabel || "Copy"; }, 1200);
|
||||
});
|
||||
}
|
||||
for (const btn of out.querySelectorAll("button[data-verify-registration]")) {
|
||||
btn.addEventListener("click", runHostStatus);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user