// ---------- 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 = `
!${escapeHtml(emptyLabel)}no checks returned
`; 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 = `#summary${checks.length} checks · ok ${counts.ok || 0} · info ${counts.info || 0} · warn ${counts.warn || 0} · fail ${counts.fail || 0}`; 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 = `${icon}${escapeHtml(c.name)}${formatDiagDetail(c.detail || "")}`; 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 `
${escapeHtml(first)}${text.length > first.length ? "…" : ""}
${escapeHtml(text)}
`; } async function runDiagnostics() { const out = document.getElementById("diag-results"); clearNativeRepairCard(); out.innerHTML = '
running…waiting for native host
'; 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 = `
runtime${escapeHtml(err.message || String(err))}
`; return { ok: false }; } } async function runHostStatus() { const out = document.getElementById("host-status-results"); clearNativeRepairCard(); out.innerHTML = '
checking…reading manifest and registry state
'; 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 = `
host status${escapeHtml(err.message || String(err))}
`; return { ok: false }; } } async function runHostRepair() { const out = document.getElementById("host-status-results"); clearNativeRepairCard(); out.innerHTML = '
repairing…updating reachable native host manifest and user registration
'; 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 = `
Registration repair${escapeHtml(r?.error || "repair failed")}
`; } return { ok: false }; } const checks = r.verification?.checks || []; renderDiagRows(out, checks, "repair verification"); renderCompletedNativeRepair(r); return { ok: true }; } catch (err) { out.innerHTML = `
Registration repair${escapeHtml(err.message || String(err))}
`; 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 = `
Repair applied${escapeHtml(response.message || "native host registration repaired")}
iManifest${escapeHtml(response.manifest_path || "")}
iUser registry${escapeHtml(`${regs} HKCU registration entr${regs === 1 ? "y" : "ies"} updated`)}
!Restart requiredFully close Brave, reopen it, reload the extension, then click Verify Registration. If Brave still blocks the host, run the registration steps shown by Diagnostics.
`; } function renderBlockedByNativeIssue(out, title) { out.innerHTML = `
i${escapeHtml(title)}Blocked until this PC registers the native host for the current extension ID. Use the setup card above.
`; } 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 = `
!Setup requiredNative host registration must be fixed before cache, runtime, and host checks can run.
!Likely cause${escapeHtml(cause)}
iHost message${escapeHtml(error)}
Fix on this PC${escapeHtml(fix)}
iExtension ID${escapeHtml(extensionId)}
1Run register-host
${escapeHtml(registerCommand)}
${escapeHtml(`Run ${registerCommand}\nWhen it asks for the extension ID, enter:\n${extensionId}\n\nPowerShell alternative:\n${installCommand}`)}
2Restart BraveClose every Brave window/process, reopen Brave, then reload the extension.
3Verify
`; 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); } }