From f2de7dec610c44bc68922ffb6d6ef11f9f5af810 Mon Sep 17 00:00:00 2001 From: serversdown Date: Sun, 21 Jun 2026 05:27:55 +0000 Subject: [PATCH] feat(web): shared left-sidebar navigation across all pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Desktop nav was scattered and inconsistent — the chat header was crammed with cross-page links and each standalone page had its own ad-hoc, incomplete back-links (e.g. /hands could only reach Chat). Now a single nav.js (one source of truth, no build step) injects a left sidebar on desktop (>=769px) with active-page highlighting across Chat/Session/History/Hands/Mind/Journal/Logs + Settings. - nav.js: injects sidebar + its own CSS; body gets padding-left on desktop; hidden on mobile (each page keeps its bottom bar / back-links there). - Included on every page (index, session, history, hands, self, journal, logs, recap, hand). - Decluttered the chat header: removed the now-redundant cross-page links (kept the chat-specific session selector + inline Live Log toggle). - Sidebar Settings opens the chat modal, or navigates to /?settings=1 from elsewhere. Co-Authored-By: Claude Opus 4.8 (1M context) --- lyra/web/static/hand.html | 1 + lyra/web/static/hands.html | 1 + lyra/web/static/history.html | 1 + lyra/web/static/index.html | 9 ++--- lyra/web/static/journal.html | 1 + lyra/web/static/logs.html | 1 + lyra/web/static/nav.js | 76 ++++++++++++++++++++++++++++++++++++ lyra/web/static/recap.html | 1 + lyra/web/static/self.html | 1 + lyra/web/static/session.html | 1 + 10 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 lyra/web/static/nav.js diff --git a/lyra/web/static/hand.html b/lyra/web/static/hand.html index 7c5f1df..6a9b05b 100644 --- a/lyra/web/static/hand.html +++ b/lyra/web/static/hand.html @@ -247,5 +247,6 @@ } load(); + diff --git a/lyra/web/static/hands.html b/lyra/web/static/hands.html index f6d3e36..4bfa51e 100644 --- a/lyra/web/static/hands.html +++ b/lyra/web/static/hands.html @@ -80,5 +80,6 @@ } load(); + diff --git a/lyra/web/static/history.html b/lyra/web/static/history.html index 43224c1..f46d364 100644 --- a/lyra/web/static/history.html +++ b/lyra/web/static/history.html @@ -100,5 +100,6 @@ } load(); + diff --git a/lyra/web/static/index.html b/lyra/web/static/index.html index a77a5d6..08d6771 100644 --- a/lyra/web/static/index.html +++ b/lyra/web/static/index.html @@ -80,11 +80,6 @@ - ⛶ Full Log - 🎬 Session - 📚 Sessions - 🧠 Mind - 🃏 Hands @@ -990,6 +985,9 @@ loadSessionList(); // Refresh session list when opening settings }); + // Sidebar "Settings" from another page navigates here with ?settings=1. + if (new URLSearchParams(location.search).get("settings")) settingsBtn.click(); + // Hide modal functions const hideModal = () => { settingsModal.classList.remove("show"); @@ -1187,5 +1185,6 @@ }); }); + diff --git a/lyra/web/static/journal.html b/lyra/web/static/journal.html index 89a9c73..7979751 100644 --- a/lyra/web/static/journal.html +++ b/lyra/web/static/journal.html @@ -157,5 +157,6 @@ setInterval(load, 20000); document.addEventListener('visibilitychange', () => { if (!document.hidden) load(); }); + diff --git a/lyra/web/static/logs.html b/lyra/web/static/logs.html index b368336..457d7be 100644 --- a/lyra/web/static/logs.html +++ b/lyra/web/static/logs.html @@ -235,5 +235,6 @@ } connect(); + diff --git a/lyra/web/static/nav.js b/lyra/web/static/nav.js new file mode 100644 index 0000000..9ce057b --- /dev/null +++ b/lyra/web/static/nav.js @@ -0,0 +1,76 @@ +/* Shared app navigation — one source of truth across all pages (no build step). + Injects a left sidebar on desktop (>=769px) with active-page highlighting; stays + out of the way on mobile, where each page keeps its bottom bar / back-links. */ +(function () { + const ITEMS = [ + { href: "/", icon: "💬", label: "Chat" }, + { href: "/session", icon: "♠", label: "Session" }, + { href: "/history", icon: "📚", label: "History" }, + { href: "/hands", icon: "🃏", label: "Hands" }, + { href: "/self", icon: "🧠", label: "Mind" }, + { href: "/journal", icon: "📔", label: "Journal" }, + { href: "/logs", icon: "📜", label: "Logs" }, + ]; + + const path = location.pathname; + function isActive(href) { + if (href === "/") return path === "/" || path === ""; + if (href === "/hands") return path === "/hands" || path.indexOf("/hand") === 0; + if (href === "/history") return path.indexOf("/history") === 0 || path.indexOf("/recap") === 0; + return path === href || path.indexOf(href + "/") === 0; + } + + const css = ` + #app-nav { display: none; } + @media screen and (min-width: 769px) { + body { padding-left: 212px; } + #app-nav { + position: fixed; left: 0; top: 0; bottom: 0; width: 212px; z-index: 1000; + display: flex; flex-direction: column; gap: 2px; box-sizing: border-box; + padding: 14px 10px; background: #0b0b0b; border-right: 1px solid #2a1d12; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + } + #app-nav .brand { + display: flex; align-items: center; gap: 8px; text-decoration: none; + color: #ff7a00; font-weight: 700; font-size: 1.15rem; letter-spacing: .5px; + padding: 6px 11px 14px; + } + #app-nav .brand .dot { width: 8px; height: 8px; border-radius: 50%; + background: #8fd694; box-shadow: 0 0 8px rgba(143,214,148,.6); } + #app-nav .navitem { + display: flex; align-items: center; gap: 11px; width: 100%; text-align: left; + padding: 9px 11px; border-radius: 9px; border: none; background: none; + color: #cfcfcf; text-decoration: none; font-size: .95rem; cursor: pointer; + font-family: inherit; -webkit-tap-highlight-color: transparent; + } + #app-nav .navitem .i { font-size: 1.05rem; width: 20px; text-align: center; filter: grayscale(.3); } + #app-nav .navitem:hover { background: rgba(255,122,0,.08); color: #fff; } + #app-nav .navitem.active { background: rgba(255,122,0,.14); color: #ff7a00; } + #app-nav .navitem.active .i { filter: none; } + #app-nav .spacer { flex: 1; } + }`; + + const style = document.createElement("style"); + style.textContent = css; + document.head.appendChild(style); + + const nav = document.createElement("nav"); + nav.id = "app-nav"; + nav.setAttribute("aria-label", "App navigation"); + nav.innerHTML = + ' Lyra' + + ITEMS.map(function (it) { + return '' + + '' + it.icon + '' + it.label + ""; + }).join("") + + '
' + + ''; + document.body.insertBefore(nav, document.body.firstChild); + + // Settings opens the chat-page modal; from other pages, jump to chat and open it. + nav.querySelector("#navSettings").addEventListener("click", function () { + const btn = document.getElementById("settingsBtn"); + if (btn) btn.click(); + else location.href = "/?settings=1"; + }); +})(); diff --git a/lyra/web/static/recap.html b/lyra/web/static/recap.html index e257756..613067d 100644 --- a/lyra/web/static/recap.html +++ b/lyra/web/static/recap.html @@ -74,5 +74,6 @@ } load(); + diff --git a/lyra/web/static/self.html b/lyra/web/static/self.html index f959e8a..be0da2f 100644 --- a/lyra/web/static/self.html +++ b/lyra/web/static/self.html @@ -195,5 +195,6 @@ setInterval(refresh, 12000); document.addEventListener('visibilitychange', () => { if (!document.hidden) refresh(); }); + diff --git a/lyra/web/static/session.html b/lyra/web/static/session.html index dbc674c..8b7c943 100644 --- a/lyra/web/static/session.html +++ b/lyra/web/static/session.html @@ -355,5 +355,6 @@ if (!SID) setInterval(refresh, 5000); // live HUD polls; a past session is static document.addEventListener('visibilitychange', () => { if (!document.hidden) refresh(); }); +