Add browser search and richer URL matching

This commit is contained in:
Joe Julian
2026-04-23 20:36:17 -07:00
parent c7d35927f3
commit 4afbc3c933
12 changed files with 418 additions and 25 deletions
+15
View File
@@ -697,6 +697,21 @@ if (isNodeTestEnv) {
await refreshActivePage({ force: true }).catch(() => null);
sendResponse({ success: true });
return;
case "keepassgo-search-logins": {
const settings = await loadSettings();
const response = await connectNative({
action: "search-logins",
bearerToken: settings.bearerToken,
query: String(message?.query || "").trim()
});
sendResponse({
success: Boolean(response?.success),
error: response?.error || "",
results: Array.isArray(response?.searchResults) ? response.searchResults : [],
status: response?.status ?? null
});
return;
}
case "keepassgo-page-ready":
if (Number.isInteger(sender?.tab?.id)) {
sendResponse(await refreshPageState(sender.tab.id, sender.tab.url, {
+8
View File
@@ -24,6 +24,14 @@
<h2>Matches</h2>
<div id="matches" class="match-list"></div>
</section>
<section class="search-section">
<h2>Search Vault</h2>
<form id="search-form" class="search-form">
<input id="search-query" type="search" placeholder="Search entries" autocomplete="off">
<button type="submit">Search</button>
</form>
<div id="search-results" class="match-list"></div>
</section>
</main>
<script src="popup.js"></script>
</body>
+57 -7
View File
@@ -43,21 +43,19 @@ function matchSubtitle(match) {
return parts.join(" · ") || "No username";
}
function renderMatches(state) {
const root = document.getElementById("matches");
function renderMatchList(root, matches, options = {}) {
const targetTabID = popupTabID();
const emptyMessage = options.emptyMessage || "No matching entries.";
root.textContent = "";
if (!Array.isArray(state.matches) || state.matches.length === 0) {
if (!Array.isArray(matches) || 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.";
empty.textContent = emptyMessage;
root.appendChild(empty);
return;
}
for (const match of state.matches) {
for (const match of matches) {
const row = document.createElement("button");
row.type = "button";
row.className = "match-row";
@@ -98,6 +96,28 @@ function renderMatches(state) {
}
}
function renderMatches(state) {
const emptyMessage = state.pageHasLoginForm
? "No matching entries for this page."
: "No login fields detected on this page.";
renderMatchList(document.getElementById("matches"), state.matches, { emptyMessage });
}
function renderSearchResults(results, query) {
const root = document.getElementById("search-results");
if (!query) {
root.textContent = "";
const hint = document.createElement("p");
hint.className = "subtle";
hint.textContent = "Search all entries you can access with this token.";
root.appendChild(hint);
return;
}
renderMatchList(root, results, {
emptyMessage: `No entries matched "${query}".`
});
}
function renderPageHint(state) {
const hint = document.getElementById("page-hint");
if (state.pendingFill) {
@@ -124,8 +144,38 @@ function popupTabID() {
return Number.isInteger(parsed) ? parsed : null;
}
async function searchVault(event) {
event.preventDefault();
const query = document.getElementById("search-query").value.trim();
const resultsRoot = document.getElementById("search-results");
if (!query) {
renderSearchResults([], "");
return;
}
resultsRoot.textContent = "";
const loading = document.createElement("p");
loading.className = "subtle";
loading.textContent = "Searching KeePassGO…";
resultsRoot.appendChild(loading);
try {
const response = await runtimeSend({
type: "keepassgo-search-logins",
query
});
if (!response?.success) {
throw new Error(response?.error || "Search failed.");
}
renderSearchResults(Array.isArray(response.results) ? response.results : [], query);
} catch (error) {
renderSearchResults([], query);
setStatus("Search failed", error instanceof Error ? error.message : String(error), "error");
}
}
async function main() {
try {
document.getElementById("search-form").addEventListener("submit", searchVault);
renderSearchResults([], "");
const state = await runtimeSend({
type: "keepassgo-popup-state",
force: true,
+11
View File
@@ -96,6 +96,17 @@ h2 {
gap: 8px;
}
.search-section {
margin-top: 16px;
}
.search-form {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 8px;
margin-bottom: 10px;
}
.match-row,
button,
.link-button {