ad4df28a66
New files: bulk-check.html, bulk-check.js, bulk-check.css Popup gains a 📋 launcher button next to the ⚙ Options gear. Clicking it sends `open-bulk-check` to background.js and closes the popup; background.js owns window lifecycle: - chrome.storage.session.bulkCheckWindowId stashes the open window id - existing id → chrome.windows.update({ focused, drawAttention }) - missing or stale id → chrome.windows.create({ type:'popup', width:640, height:540 }) and stash the new id - chrome.windows.onRemoved clears the stale id on close Last-paste persisted to chrome.storage.local.bulkCheckLastPaste, debounced 500ms on input, restored on window open. quickMode is read from settings at run time, matching previous behavior. Ctrl/Cmd+Enter inside the textarea triggers the check. Options page no longer carries the Bulk ID Check fieldset: removed from options.html (Library Review pdesc updated to note the relocation) and the matching handlers from options.js (1903 → 1852 lines). No manifest permission changes — own-page chrome.windows.create needs no extra permission. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
134 lines
4.6 KiB
JavaScript
134 lines
4.6 KiB
JavaScript
// Standalone Bulk ID Check window.
|
|
// Launched via chrome.windows.create from popup; background.js owns
|
|
// the windowId lifecycle (chrome.storage.session.bulkCheckWindowId).
|
|
|
|
const LAST_PASTE_KEY = "bulkCheckLastPaste";
|
|
const SAVE_DEBOUNCE_MS = 500;
|
|
|
|
function escapeHtml(s) {
|
|
return String(s ?? "").replace(/[&<>"']/g, (c) =>
|
|
({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
|
}
|
|
|
|
function readBulkIds() {
|
|
return [...new Set(document.getElementById("bulk-id-input").value
|
|
.split(/[\s,]+/)
|
|
.map((x) => x.trim())
|
|
.filter(Boolean))];
|
|
}
|
|
|
|
function updateCountPill() {
|
|
const ids = readBulkIds();
|
|
const pill = document.getElementById("count-pill");
|
|
pill.textContent = ids.length ? `${ids.length} unique ID${ids.length === 1 ? "" : "s"}` : "";
|
|
}
|
|
|
|
function renderBulkResults(r) {
|
|
const out = document.getElementById("bulk-id-results");
|
|
if (!r || !r.ok) {
|
|
out.innerHTML = `<span style="color:#faa;">error:</span> ${escapeHtml(r?.error || "no response")}`;
|
|
return;
|
|
}
|
|
const rows = [
|
|
`<div><span style="color:#777;">Mode:</span> ${escapeHtml(r.search_mode || "?")} · <span style="color:#777;">Queries:</span> ${escapeHtml(r.query_count || 0)} · <span style="color:#777;">Hits:</span> ${escapeHtml(r.hits || 0)} · <span style="color:#777;">Host:</span> ${escapeHtml(r.timings?.host_rcjav_ms ?? "?")}ms</div>`,
|
|
];
|
|
for (const q of r.queries || []) {
|
|
const hit = q.hits > 0;
|
|
const sample = (q.structured || []).slice(0, 3).map((h) => h.full_path || h.path || h.jav_id).join(" | ");
|
|
rows.push(`<div style="margin-top:7px;">
|
|
<span style="color:${hit ? "#afa" : "#ffa"};font-weight:600;">${hit ? "HIT" : "MISS"}</span>
|
|
<span>${escapeHtml(q.query || "?")}</span> · ${escapeHtml(q.hits || 0)} hit(s)
|
|
${sample ? `<div style="color:#777;margin-left:12px;">${escapeHtml(sample)}</div>` : `<div style="color:#777;margin-left:12px;">${escapeHtml(q.no_match_title || "No library hit")}</div>`}
|
|
</div>`);
|
|
}
|
|
out.innerHTML = rows.join("");
|
|
}
|
|
|
|
async function refreshModePill() {
|
|
const pill = document.getElementById("mode-pill");
|
|
try {
|
|
const settings = await chrome.runtime.sendMessage({ type: "get-settings" });
|
|
const mode = settings?.quickMode !== false ? "LIVE" : "CACHE";
|
|
pill.textContent = mode;
|
|
pill.dataset.mode = mode;
|
|
} catch {
|
|
pill.textContent = "?";
|
|
}
|
|
}
|
|
|
|
let _saveTimer = null;
|
|
function schedulePersist() {
|
|
if (_saveTimer) clearTimeout(_saveTimer);
|
|
_saveTimer = setTimeout(async () => {
|
|
_saveTimer = null;
|
|
const value = document.getElementById("bulk-id-input").value;
|
|
try {
|
|
await chrome.storage.local.set({ [LAST_PASTE_KEY]: value });
|
|
} catch { /* ignore */ }
|
|
}, SAVE_DEBOUNCE_MS);
|
|
}
|
|
|
|
async function restoreLastPaste() {
|
|
try {
|
|
const stored = await chrome.storage.local.get(LAST_PASTE_KEY);
|
|
const value = stored?.[LAST_PASTE_KEY];
|
|
if (typeof value === "string" && value) {
|
|
document.getElementById("bulk-id-input").value = value;
|
|
updateCountPill();
|
|
}
|
|
} catch { /* ignore */ }
|
|
}
|
|
|
|
document.getElementById("bulk-id-input").addEventListener("input", () => {
|
|
updateCountPill();
|
|
schedulePersist();
|
|
});
|
|
|
|
document.getElementById("bulk-id-clear").addEventListener("click", () => {
|
|
document.getElementById("bulk-id-input").value = "";
|
|
document.getElementById("bulk-id-results").innerHTML = "";
|
|
updateCountPill();
|
|
schedulePersist();
|
|
});
|
|
|
|
document.getElementById("bulk-id-run").addEventListener("click", async () => {
|
|
const runBtn = document.getElementById("bulk-id-run");
|
|
const out = document.getElementById("bulk-id-results");
|
|
const queries = readBulkIds();
|
|
if (!queries.length) {
|
|
out.innerHTML = `<span style="color:#ffa;">paste at least one ID</span>`;
|
|
return;
|
|
}
|
|
runBtn.disabled = true;
|
|
runBtn.classList.add("running");
|
|
out.textContent = `checking ${queries.length} ID(s)...`;
|
|
try {
|
|
const settings = await chrome.runtime.sendMessage({ type: "get-settings" });
|
|
const quick = settings?.quickMode !== false;
|
|
const r = await chrome.runtime.sendMessage({
|
|
type: "bulk-query",
|
|
queries,
|
|
quick,
|
|
});
|
|
renderBulkResults(r);
|
|
} catch (err) {
|
|
out.innerHTML = `<span style="color:#faa;">error:</span> ${escapeHtml(err?.message || String(err))}`;
|
|
} finally {
|
|
runBtn.disabled = false;
|
|
runBtn.classList.remove("running");
|
|
}
|
|
});
|
|
|
|
// Ctrl/Cmd+Enter inside textarea = run
|
|
document.getElementById("bulk-id-input").addEventListener("keydown", (e) => {
|
|
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
|
e.preventDefault();
|
|
document.getElementById("bulk-id-run").click();
|
|
}
|
|
});
|
|
|
|
(async () => {
|
|
await restoreLastPaste();
|
|
await refreshModePill();
|
|
})();
|