const extPopup = globalThis.browser ?? globalThis.chrome; const usePromiseAPI = typeof globalThis.browser !== "undefined"; function runtimeSend(message) { if (usePromiseAPI) { return extPopup.runtime.sendMessage(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 matchSubtitle(match) { const parts = []; if (match.username) { parts.push(match.username); } if (Array.isArray(match.path) && match.path.length !== 0) { parts.push(match.path.join(" / ")); } return parts.join(" ยท ") || "No username"; } function renderMatches(state) { const root = document.getElementById("matches"); const targetTabID = popupTabID(); root.textContent = ""; if (!Array.isArray(state.matches) || state.matches.length === 0) { const empty = document.createElement("p"); empty.className = "subtle"; empty.textContent = state.pageHasLoginForm ? "No matching entries for this page." : "No login fields detected on this page."; root.appendChild(empty); return; } for (const match of state.matches) { const row = document.createElement("button"); row.type = "button"; row.className = "match-row"; const main = document.createElement("span"); main.className = "match-main"; const title = document.createElement("strong"); title.textContent = match.title; const subtitle = document.createElement("span"); subtitle.className = "subtle"; subtitle.textContent = matchSubtitle(match); const quality = document.createElement("span"); quality.className = "quality"; quality.textContent = match.quality || ""; main.appendChild(title); main.appendChild(subtitle); row.appendChild(main); row.appendChild(quality); row.addEventListener("click", async () => { row.disabled = true; setStatus("Approval may be required", "KeePassGO will prompt if this token needs approval before fill.", "warning"); try { const result = await runtimeSend({ type: "keepassgo-fill-entry", entryId: match.id, tabId: targetTabID }); 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); } } function renderPageHint(state) { const hint = document.getElementById("page-hint"); if (state.pendingFill) { hint.textContent = "Approval is pending in KeePassGO."; return; } if (state.pageHasLoginForm && Array.isArray(state.matches) && state.matches.length > 0) { hint.textContent = "Inline KeePassGO suggestions are available on the page."; return; } if (state.pageHasLoginForm) { hint.textContent = "KeePassGO checked this login form already."; return; } hint.textContent = "Open a sign-in page to see KeePassGO suggestions here."; } function popupTabID() { const rawValue = new URLSearchParams(window.location.search).get("tabId"); if (rawValue === null) { return null; } const parsed = Number.parseInt(rawValue, 10); return Number.isInteger(parsed) ? parsed : null; } async function main() { try { const state = await runtimeSend({ type: "keepassgo-popup-state", force: true, tabId: popupTabID() }); document.getElementById("page-host").textContent = hostFromURL(state.pageUrl || ""); renderPageHint(state); if (!state.configured) { setStatus("Configure access", state.error || "Set the API token in extension settings.", "warning"); renderMatches({ matches: [] }); return; } if (state.pendingFill) { setStatus("Approval needed", state.pendingMessage || "Approve or deny the fill request in KeePassGO.", "warning"); renderMatches(state); return; } if (!state.success) { setStatus("KeePassGO unavailable", state.error || "The native host could not reach KeePassGO.", "error"); renderMatches(state); return; } if (state.status?.locked) { setStatus("Vault locked", "Unlock KeePassGO, then try the page again.", "warning"); renderMatches(state); return; } const count = Array.isArray(state.matches) ? state.matches.length : 0; if (!state.pageHasLoginForm) { setStatus("Ready", "KeePassGO is connected. Open a login form to check for matches.", "ready"); } else if (count === 0) { setStatus("Checked this page", "KeePassGO did not find a matching login for this form.", "ready"); } else { setStatus("Page suggestions ready", count === 1 ? "1 matching entry is ready on this page." : `${count} matching entries are ready on this page.`, "ready"); } renderMatches(state); } catch (error) { setStatus("Error", error instanceof Error ? error.message : String(error), "error"); renderMatches({ matches: [] }); } } void main();