d0a2def788
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>
246 lines
13 KiB
JavaScript
246 lines
13 KiB
JavaScript
// ---------- 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);
|
|
}
|
|
}
|
|
|