Add browser search and richer URL matching
This commit is contained in:
@@ -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, {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user