|
|
|
@@ -524,6 +524,7 @@ export function initSearch() {
|
|
|
|
|
const caseBtn = document.getElementById("rb-case");
|
|
|
|
|
const wordBtn = document.getElementById("rb-word");
|
|
|
|
|
const regexBtn = document.getElementById("rb-regex");
|
|
|
|
|
const semanticBtn = document.getElementById("rb-semantic");
|
|
|
|
|
const filterBtn = document.getElementById("search-filter-btn");
|
|
|
|
|
const clearBtn = document.getElementById("search-clear-btn");
|
|
|
|
|
const filterRow = document.getElementById("search-filter-row");
|
|
|
|
@@ -540,12 +541,25 @@ export function initSearch() {
|
|
|
|
|
wordBtn.classList.toggle("active", state.searchWholeWord);
|
|
|
|
|
regexBtn.classList.toggle("active", state.searchRegex);
|
|
|
|
|
filterBtn.classList.toggle("active", state.searchFilterVisible);
|
|
|
|
|
if (semanticBtn) {
|
|
|
|
|
semanticBtn.classList.toggle("active", state.semanticSearch);
|
|
|
|
|
semanticBtn.disabled = !state.semanticAvailable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Toggle buttons
|
|
|
|
|
caseBtn.addEventListener("click", () => { state.searchCaseSensitive = !state.searchCaseSensitive; _updateToggleUI(); _research(); });
|
|
|
|
|
if (wordBtn) wordBtn.addEventListener("click", () => { state.searchWholeWord = !state.searchWholeWord; _updateToggleUI(); _research(); });
|
|
|
|
|
if (regexBtn) regexBtn.addEventListener("click", () => { state.searchRegex = !state.searchRegex; _updateToggleUI(); _research(); });
|
|
|
|
|
if (semanticBtn) {
|
|
|
|
|
semanticBtn.title = t("search.semantic_title");
|
|
|
|
|
semanticBtn.addEventListener("click", () => {
|
|
|
|
|
if (!state.semanticAvailable) return;
|
|
|
|
|
state.semanticSearch = !state.semanticSearch;
|
|
|
|
|
_updateToggleUI();
|
|
|
|
|
_research();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (filterBtn) filterBtn.addEventListener("click", () => { state.searchFilterVisible = !state.searchFilterVisible; if (filterRow) filterRow.style.display = state.searchFilterVisible ? "flex" : "none"; _updateToggleUI(); });
|
|
|
|
|
|
|
|
|
|
// ── Result navigation (up/down arrows + Enter) ──
|
|
|
|
@@ -599,6 +613,7 @@ export function initSearch() {
|
|
|
|
|
if (e.key === "c" || e.key === "C") { e.preventDefault(); caseBtn.click(); }
|
|
|
|
|
else if (e.key === "w" || e.key === "W") { e.preventDefault(); if (wordBtn) wordBtn.click(); }
|
|
|
|
|
else if (e.key === "r" || e.key === "R") { e.preventDefault(); if (regexBtn) regexBtn.click(); }
|
|
|
|
|
else if (e.key === "s" || e.key === "S") { e.preventDefault(); if (semanticBtn) semanticBtn.click(); }
|
|
|
|
|
else if (e.key === "f" || e.key === "F") { e.preventDefault(); if (filterBtn) filterBtn.click(); input.focus(); }
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
@@ -788,6 +803,7 @@ export async function performAdvancedSearch(query, vaultFilter, tagFilter, offse
|
|
|
|
|
if (parsed.created) url += `&created=${encodeURIComponent(parsed.created)}`;
|
|
|
|
|
if (parsed.modified) url += `&modified=${encodeURIComponent(parsed.modified)}`;
|
|
|
|
|
if (parsed.size) url += `&size=${encodeURIComponent(parsed.size)}`;
|
|
|
|
|
if (state.semanticSearch) url += "&semantic=true";
|
|
|
|
|
|
|
|
|
|
// Search timeout — abort if server takes too long
|
|
|
|
|
const timeoutId = setTimeout(
|
|
|
|
@@ -803,6 +819,12 @@ export async function performAdvancedSearch(query, vaultFilter, tagFilter, offse
|
|
|
|
|
if (searchId !== state.currentSearchId) return;
|
|
|
|
|
state.advancedSearchTotal = data.total;
|
|
|
|
|
state.advancedSearchOffset = ofs;
|
|
|
|
|
state.semanticAvailable = !!data.semantic_available;
|
|
|
|
|
const semBtn = document.getElementById("rb-semantic");
|
|
|
|
|
if (semBtn) {
|
|
|
|
|
semBtn.disabled = !state.semanticAvailable;
|
|
|
|
|
semBtn.classList.toggle("active", state.semanticSearch);
|
|
|
|
|
}
|
|
|
|
|
// Plugin hook: filter search results
|
|
|
|
|
data.results = await onSearchFilter(state.vault || "default", query, data.results);
|
|
|
|
|
renderAdvancedSearchResults(data, query, tagFilter);
|
|
|
|
@@ -1092,7 +1114,9 @@ export function renderAdvancedSearchResults(data, query, tagFilter) {
|
|
|
|
|
|
|
|
|
|
// Score badge
|
|
|
|
|
const scoreEl = el("span", { class: "search-result-score", style: "font-size:0.7rem;color:var(--text-muted);margin-left:8px" });
|
|
|
|
|
scoreEl.textContent = `score: ${r.score}`;
|
|
|
|
|
let scoreText = `score: ${r.score}`;
|
|
|
|
|
if (r.semantic_score) scoreText += ` · ${t("search.semantic_score")}: ${r.semantic_score}`;
|
|
|
|
|
scoreEl.textContent = scoreText;
|
|
|
|
|
|
|
|
|
|
const vaultPath = el("div", { class: "search-result-vault" }, [document.createTextNode(r.vault + " / " + r.path), scoreEl]);
|
|
|
|
|
|
|
|
|
|