// svrnty_nav.js — injects Adwright + BTE sidebar buttons into hermes-webui's // .sidebar-nav and wraps switchPanel so our panels participate in the existing // main-view show/hide system (showing- on
). // // Each panel module (adwright.js, bte.js) mounts its content inside
// and CSS keys visibility off main.showing-adwright / main.showing-bte. // // IIFE, idempotent — guarded by window.__svrntyNavLoaded. (function () { "use strict"; if (window.__svrntyNavLoaded) return; window.__svrntyNavLoaded = true; const LOG = (...a) => console.log("[svrnty-nav]", ...a); const ERR = (...a) => console.error("[svrnty-nav]", ...a); LOG("loaded", { readyState: document.readyState }); // SVG paths only — sizes/strokes templated per container (rail uses 20×20 // stroke 1.5; sidebar-nav uses 18×18 stroke 2). Matches existing buttons. const ICONS = { adwright: '', bte: '', }; const TABS = [ { id: "adwright", label: "Adwright", tooltip: "Adwright — marketing intelligence" }, { id: "bte", label: "BTE", tooltip: "BTE — brand creative studio" }, ]; function _svg(iconPath, size, stroke) { return ( '" ); } // Two containers: .rail (desktop, .rail-btn nav-tab 20×20 stroke-1.5) + // .sidebar-nav (mobile, .nav-tab 18×18 stroke-2). Inject into BOTH if present. const CONTAINERS = [ { selector: ".rail", btnClass: "rail-btn nav-tab has-tooltip svrnty-nav-tab", size: 20, stroke: 1.5, tooltipMod: "", }, { selector: ".sidebar-nav", btnClass: "nav-tab has-tooltip has-tooltip--bottom svrnty-nav-tab", size: 18, stroke: 2, tooltipMod: "--bottom", }, ]; function _injectButtons() { let anyContainerFound = false; let totalAdded = 0; CONTAINERS.forEach((c) => { const container = document.querySelector(c.selector); if (!container) return; anyContainerFound = true; TABS.forEach((t) => { if (container.querySelector('[data-panel="' + t.id + '"]')) return; try { const btn = document.createElement("button"); btn.className = c.btnClass; btn.setAttribute("data-panel", t.id); btn.setAttribute("data-label", t.label); btn.setAttribute("data-tooltip", t.tooltip); btn.setAttribute("aria-label", t.label); btn.innerHTML = _svg(ICONS[t.id], c.size, c.stroke); btn.addEventListener("click", () => { LOG("clicked:", t.id); if (typeof window.switchPanel === "function") { window.switchPanel(t.id, { fromRailClick: true }); } else { ERR("switchPanel undefined — cannot route click"); } }); container.appendChild(btn); totalAdded++; } catch (e) { ERR("inject failed", c.selector, t.id, e); } }); }); if (!anyContainerFound) { LOG("_injectButtons: neither .rail nor .sidebar-nav present yet"); return false; } LOG("_injectButtons: added", totalAdded, "buttons across containers"); return true; } // Wrap switchPanel to add showing- class on
for our IDs. // We chain the original so all upstream behavior (collapse rail, hide other // panels, toggle data-panel active) keeps working unchanged. function _wrapSwitchPanel() { if (typeof window.switchPanel !== "function") return false; if (window.switchPanel.__svrntyWrapped) return true; const original = window.switchPanel; const OUR_IDS = TABS.map((t) => t.id); async function wrapped(name, opts) { const result = await original(name, opts); const main = document.querySelector("main.main"); if (main) { OUR_IDS.forEach((id) => { main.classList.toggle("svrnty-showing-" + id, name === id); }); } // Notify panel modules so they can lazy-init/refresh. try { window.dispatchEvent( new CustomEvent("svrnty:panel-switch", { detail: { name } }), ); } catch (_) {} return result; } wrapped.__svrntyWrapped = true; window.switchPanel = wrapped; return true; } let _initAttempts = 0; function _init() { _initAttempts++; const buttonsOk = _injectButtons(); const wrapOk = _wrapSwitchPanel(); if (!buttonsOk || !wrapOk) { if (_initAttempts < 100) { requestAnimationFrame(_init); } else { ERR("_init gave up after", _initAttempts, "attempts. buttonsOk=", buttonsOk, "wrapOk=", wrapOk); } return; } LOG("_init completed in", _initAttempts, "attempt(s)"); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", _init); } else { _init(); } // Re-inject buttons if something re-renders rail or sidebar-nav (defensive). const obs = new MutationObserver(() => { CONTAINERS.forEach((c) => { const container = document.querySelector(c.selector); if (!container) return; const missing = TABS.some((t) => !container.querySelector('[data-panel="' + t.id + '"]')); if (missing) _injectButtons(); }); }); if (document.body) { obs.observe(document.body, { childList: true, subtree: true }); } else { document.addEventListener("DOMContentLoaded", () => obs.observe(document.body, { childList: true, subtree: true }), ); } // Expose namespace window.SvrntyNav = { TABS, _injectButtons, _wrapSwitchPanel }; })();