102 lines
3.2 KiB
JavaScript
102 lines
3.2 KiB
JavaScript
const extPopup = globalThis.browser ?? globalThis.chrome;
|
|
|
|
function runtimeSend(message) {
|
|
return new Promise((resolve, reject) => {
|
|
extPopup.runtime.sendMessage(message, (response) => {
|
|
const error = extPopup.runtime.lastError;
|
|
if (error) {
|
|
reject(new Error(error.message));
|
|
return;
|
|
}
|
|
resolve(response);
|
|
});
|
|
});
|
|
}
|
|
|
|
function hostFromURL(rawURL) {
|
|
try {
|
|
return new URL(rawURL).host || rawURL;
|
|
} catch (_error) {
|
|
return rawURL || "Current page";
|
|
}
|
|
}
|
|
|
|
function setStatus(title, message, tone) {
|
|
const card = document.getElementById("status-card");
|
|
card.dataset.tone = tone || "neutral";
|
|
document.getElementById("status-title").textContent = title;
|
|
document.getElementById("status-message").textContent = message;
|
|
}
|
|
|
|
function renderMatches(state) {
|
|
const root = document.getElementById("matches");
|
|
root.textContent = "";
|
|
if (!Array.isArray(state.matches) || state.matches.length === 0) {
|
|
const empty = document.createElement("p");
|
|
empty.className = "subtle";
|
|
empty.textContent = "No matching entries for this page.";
|
|
root.appendChild(empty);
|
|
return;
|
|
}
|
|
|
|
for (const match of state.matches) {
|
|
const row = document.createElement("button");
|
|
row.type = "button";
|
|
row.className = "match-row";
|
|
row.innerHTML = `
|
|
<span class="match-main">
|
|
<strong>${match.title}</strong>
|
|
<span class="subtle">${match.username || "No username"}</span>
|
|
</span>
|
|
<span class="quality">${match.quality || ""}</span>
|
|
`;
|
|
row.addEventListener("click", async () => {
|
|
row.disabled = true;
|
|
try {
|
|
const result = await runtimeSend({ type: "keepassgo-fill-entry", entryId: match.id });
|
|
if (!result?.success) {
|
|
throw new Error(result?.error || "Fill failed.");
|
|
}
|
|
setStatus("Filled", `${match.title} was sent to the current page.`, "ready");
|
|
} catch (error) {
|
|
setStatus("Fill failed", error instanceof Error ? error.message : String(error), "error");
|
|
} finally {
|
|
row.disabled = false;
|
|
}
|
|
});
|
|
root.appendChild(row);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
const state = await runtimeSend({ type: "keepassgo-popup-state" });
|
|
document.getElementById("page-host").textContent = hostFromURL(state.pageUrl || "");
|
|
|
|
if (!state.configured) {
|
|
setStatus("Configure access", state.error || "Set the API token in extension settings.", "warning");
|
|
renderMatches({ matches: [] });
|
|
return;
|
|
}
|
|
if (!state.success) {
|
|
setStatus("KeePassGO unavailable", state.error || "The native host could not reach KeePassGO.", "error");
|
|
renderMatches({ matches: [] });
|
|
return;
|
|
}
|
|
if (state.status?.locked) {
|
|
setStatus("Vault locked", "Unlock KeePassGO, then open the popup again.", "warning");
|
|
renderMatches({ matches: [] });
|
|
return;
|
|
}
|
|
|
|
const count = Array.isArray(state.matches) ? state.matches.length : 0;
|
|
setStatus("Ready", count === 0 ? "KeePassGO is connected." : `${count} matching entr${count === 1 ? "y" : "ies"} found.`, "ready");
|
|
renderMatches(state);
|
|
} catch (error) {
|
|
setStatus("Error", error instanceof Error ? error.message : String(error), "error");
|
|
renderMatches({ matches: [] });
|
|
}
|
|
}
|
|
|
|
void main();
|