diff --git a/lyra/web/static/index.html b/lyra/web/static/index.html
index 94a471c..4f60a17 100644
--- a/lyra/web/static/index.html
+++ b/lyra/web/static/index.html
@@ -41,9 +41,10 @@
Actions
+
+
-
@@ -1203,6 +1204,9 @@
document.getElementById("mobileHistoryBtn").addEventListener("click", () => {
closeMobileMenu(); window.location.href = "/history";
});
+ document.getElementById("mobileThoughtsBtn").addEventListener("click", () => {
+ closeMobileMenu(); window.location.href = "/thoughts";
+ });
// Connect to the global live log on page load.
connectThinkingStream();
diff --git a/lyra/web/static/nav.js b/lyra/web/static/nav.js
index fbd1de0..349b7e9 100644
--- a/lyra/web/static/nav.js
+++ b/lyra/web/static/nav.js
@@ -1,6 +1,7 @@
/* 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. */
+ Desktop (>=769px): a fixed left sidebar. Mobile (<=768px): a slide-in drawer
+ behind a ☰ button — but ONLY on pages that don't already ship their own mobile
+ menu (the chat page has its own hamburger + tab bar, so we leave it alone). */
(function () {
const ITEMS = [
{ href: "/", icon: "💬", label: "Chat" },
@@ -21,34 +22,45 @@
return path === href || path.indexOf(href + "/") === 0;
}
+ // Visual styling (all sizes); positioning differs per breakpoint below.
const css = `
- #app-nav { display: none; }
+ #app-nav { display: none; 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; }
+ #app-nav-burger { display: none; }
+ #app-nav-scrim { 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; }
+ #app-nav { display: flex; position: fixed; left: 0; top: 0; bottom: 0; width: 212px; z-index: 1000; }
+ }
+
+ @media screen and (max-width: 768px) {
+ body.lyra-nav-mobile #app-nav-burger { display: flex; align-items: center; justify-content: center;
+ position: fixed; top: calc(env(safe-area-inset-top) + 8px); right: 10px; z-index: 1301;
+ width: 40px; height: 40px; border-radius: 10px; border: 1px solid #2a1d12;
+ background: rgba(14,14,14,.92); color: #ff7a00; font-size: 1.2rem; cursor: pointer;
+ -webkit-tap-highlight-color: transparent; backdrop-filter: blur(4px); }
+ body.lyra-nav-mobile #app-nav { display: flex; position: fixed; left: 0; top: 0; bottom: 0;
+ width: 240px; max-width: 80vw; transform: translateX(-100%); transition: transform .22s ease;
+ z-index: 1310; padding-top: calc(env(safe-area-inset-top) + 14px); overflow-y: auto; }
+ body.lyra-nav-mobile #app-nav.open { transform: translateX(0); }
+ body.lyra-nav-mobile #app-nav-scrim.show { display: block; position: fixed; inset: 0;
+ background: rgba(0,0,0,.5); z-index: 1305; }
+ #app-nav .navitem { padding: 12px 11px; font-size: 1rem; }
}`;
const style = document.createElement("style");
@@ -74,4 +86,24 @@
if (btn) btn.click();
else location.href = "/?settings=1";
});
+
+ // Mobile drawer — only on pages without their own mobile menu (i.e., not the chat page).
+ if (!document.getElementById("hamburgerMenu")) {
+ document.body.classList.add("lyra-nav-mobile");
+ const burger = document.createElement("button");
+ burger.id = "app-nav-burger";
+ burger.type = "button";
+ burger.setAttribute("aria-label", "Menu");
+ burger.textContent = "☰";
+ const scrim = document.createElement("div");
+ scrim.id = "app-nav-scrim";
+ document.body.appendChild(burger);
+ document.body.appendChild(scrim);
+ const close = function () { nav.classList.remove("open"); scrim.classList.remove("show"); };
+ burger.addEventListener("click", function () {
+ nav.classList.toggle("open"); scrim.classList.toggle("show");
+ });
+ scrim.addEventListener("click", close);
+ nav.addEventListener("click", function (e) { if (e.target.closest("a")) close(); });
+ }
})();