| 1 | // 🤖 The shared masthead + status nav — ONE copy for all twelve dashboards. |
| 2 | // Each page ships two empty mounts and this include: |
| 3 | // <header data-title="The prompt registry"></header> |
| 4 | // <nav class="status-nav" aria-label="Status"></nav> |
| 5 | // <script src="./status-nav.js"></script> |
| 6 | // and this script builds both, marking the current page from the URL. The |
| 7 | // pages drifted while each carried its own copy of this markup (stale nav |
| 8 | // lists, local h1 overrides quietly moving the masthead) — with one builder |
| 9 | // they can't. Styles live in status.css; same-origin like everything else on |
| 10 | // the status bucket, uploaded by deploy/status-publish.sh before the pages. |
| 11 | (function () { |
| 12 | "use strict"; |
| 13 | var GROUPS = [ |
| 14 | ["Operations", [["index.html", "Fleet"], ["hardening.html", "Hardening"], ["security.html", "Security"]]], |
| 15 | ["Money", [["books.html", "Income & Expenditure"], ["economics.html", "Economics"], ["funding.html", "Investing"]]], |
| 16 | ["Ledgers", [["carbon.html", "Carbon"], ["contributors.html", "Contributors"], ["prompts.html", "Prompts"], ["ssh.html", "Access"], ["music.html", "Music"]]], |
| 17 | ["Plan", [["roadmap.html", "Roadmap"], ["milestones.html", "Milestones"]]], |
| 18 | ]; |
| 19 | var here = location.pathname.split("/").pop() || "index.html"; |
| 20 | |
| 21 | var header = document.querySelector("header[data-title]"); |
| 22 | if (header) { |
| 23 | var img = document.createElement("img"); |
| 24 | img.src = "./icon-192.png"; |
| 25 | img.alt = ""; |
| 26 | var box = document.createElement("div"); |
| 27 | var brand = document.createElement("p"); |
| 28 | brand.className = "brand"; |
| 29 | brand.textContent = "Eleven Messenger"; |
| 30 | var h1 = document.createElement("h1"); |
| 31 | h1.textContent = header.dataset.title; |
| 32 | box.append(brand, h1); |
| 33 | header.append(img, box); |
| 34 | } |
| 35 | |
| 36 | var nav = document.querySelector("nav.status-nav"); |
| 37 | if (nav) { |
| 38 | // The narrow-screen disclosure: CSS hides the groups (and shows this |
| 39 | // button) under 640px; aria-expanded tracks the .open state. |
| 40 | var toggle = document.createElement("button"); |
| 41 | toggle.type = "button"; |
| 42 | toggle.className = "menu-toggle"; |
| 43 | toggle.setAttribute("aria-expanded", "false"); |
| 44 | var glyph = document.createElement("span"); |
| 45 | glyph.className = "glyph"; |
| 46 | glyph.setAttribute("aria-hidden", "true"); |
| 47 | glyph.textContent = "☰"; |
| 48 | toggle.append(glyph, document.createTextNode("Menu")); |
| 49 | toggle.addEventListener("click", function () { |
| 50 | toggle.setAttribute("aria-expanded", String(nav.classList.toggle("open"))); |
| 51 | }); |
| 52 | nav.appendChild(toggle); |
| 53 | for (var g = 0; g < GROUPS.length; g++) { |
| 54 | var group = document.createElement("span"); |
| 55 | group.className = "group"; |
| 56 | var tag = document.createElement("span"); |
| 57 | tag.className = "group-label"; |
| 58 | tag.textContent = GROUPS[g][0]; |
| 59 | group.appendChild(tag); |
| 60 | var pages = GROUPS[g][1]; |
| 61 | for (var p = 0; p < pages.length; p++) { |
| 62 | var item; |
| 63 | if (pages[p][0] === here) { |
| 64 | item = document.createElement("span"); |
| 65 | item.setAttribute("aria-current", "page"); |
| 66 | } else { |
| 67 | item = document.createElement("a"); |
| 68 | item.href = "./" + pages[p][0]; |
| 69 | } |
| 70 | item.textContent = pages[p][1]; |
| 71 | group.appendChild(item); |
| 72 | } |
| 73 | nav.appendChild(group); |
| 74 | } |
| 75 | } |
| 76 | })(); |
| 77 | |