diff --git a/frontend/app.js b/frontend/app.js index 93ea584..fb531f0 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -368,6 +368,32 @@ return res.json(); } + // --------------------------------------------------------------------------- + // Sidebar toggle (desktop) + // --------------------------------------------------------------------------- + function initSidebarToggle() { + const toggleBtn = document.getElementById("sidebar-toggle-btn"); + const sidebar = document.getElementById("sidebar"); + const resizeHandle = document.getElementById("sidebar-resize-handle"); + + if (!toggleBtn || !sidebar || !resizeHandle) return; + + // Restore saved state + const savedState = localStorage.getItem("obsigate-sidebar-hidden"); + if (savedState === "true") { + sidebar.classList.add("hidden"); + resizeHandle.classList.add("hidden"); + toggleBtn.classList.add("active"); + } + + toggleBtn.addEventListener("click", () => { + const isHidden = sidebar.classList.toggle("hidden"); + resizeHandle.classList.toggle("hidden", isHidden); + toggleBtn.classList.toggle("active", isHidden); + localStorage.setItem("obsigate-sidebar-hidden", isHidden ? "true" : "false"); + }); + } + // --------------------------------------------------------------------------- // Mobile sidebar // --------------------------------------------------------------------------- @@ -1580,10 +1606,26 @@ const container = el("div", { class: "search-results" }); data.results.forEach((r) => { + // Create title with highlighting + const titleDiv = el("div", { class: "search-result-title" }); + if (query && query.trim()) { + highlightSearchText(titleDiv, r.title, query, searchCaseSensitive); + } else { + titleDiv.textContent = r.title; + } + + // Create snippet with highlighting + const snippetDiv = el("div", { class: "search-result-snippet" }); + if (query && query.trim() && r.snippet) { + highlightSearchText(snippetDiv, r.snippet, query, searchCaseSensitive); + } else { + snippetDiv.textContent = r.snippet || ""; + } + const item = el("div", { class: "search-result-item" }, [ - el("div", { class: "search-result-title" }, [document.createTextNode(r.title)]), + titleDiv, el("div", { class: "search-result-vault" }, [document.createTextNode(r.vault + " / " + r.path)]), - el("div", { class: "search-result-snippet" }, [document.createTextNode(r.snippet || "")]), + snippetDiv, ]); if (r.tags && r.tags.length > 0) { @@ -1764,6 +1806,40 @@ } } + function highlightSearchText(container, text, query, caseSensitive) { + container.textContent = ""; + if (!query || !text) { + container.appendChild(document.createTextNode(text || "")); + return; + } + + const source = caseSensitive ? text : text.toLowerCase(); + const needle = caseSensitive ? query : query.toLowerCase(); + let start = 0; + let index = source.indexOf(needle, start); + + if (index === -1) { + container.appendChild(document.createTextNode(text)); + return; + } + + while (index !== -1) { + if (index > start) { + container.appendChild(document.createTextNode(text.slice(start, index))); + } + const mark = el("mark", { class: "search-highlight" }, [ + document.createTextNode(text.slice(index, index + query.length)), + ]); + container.appendChild(mark); + start = index + query.length; + index = source.indexOf(needle, start); + } + + if (start < text.length) { + container.appendChild(document.createTextNode(text.slice(start))); + } + } + function showWelcome() { const area = document.getElementById("content-area"); area.innerHTML = ` @@ -2015,6 +2091,7 @@ document.getElementById("theme-toggle").addEventListener("click", toggleTheme); document.getElementById("header-logo").addEventListener("click", goHome); initSearch(); + initSidebarToggle(); initMobile(); initVaultContext(); initSidebarTabs(); diff --git a/frontend/index.html b/frontend/index.html index 02c5201..c3d1a61 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -68,6 +68,9 @@ +