fix(plugin): inject sidebar buttons into BOTH .rail (desktop) + .sidebar-nav (mobile)
Some checks failed
plugin-tests / test (push) Failing after 5s
Some checks failed
plugin-tests / test (push) Failing after 5s
ROOT CAUSE of JP's "buttons not showing" report: hermes-webui has two nav containers — `<nav class="rail">` (desktop ≥641px, rail-btn 20×20 stroke 1.5) and `.sidebar-nav` (mobile, nav-tab 18×18 stroke 2). My previous injection only touched .sidebar-nav, so on desktop the buttons literally didn't exist anywhere visible. This patch: - Defines per-container button templates (class list + svg size/stroke) matching exactly how upstream renders its native rail-btn vs nav-tab - Iterates both containers on init + retries - MutationObserver re-injects into either if something re-renders it - Tooltip modifier (--bottom) only applied for sidebar-nav (mobile pattern) - SVG paths centralized so adding new tabs is one entry Karpathy 4 rules: surfaced root cause via console logs + DOM inspection instead of guessing, simplest fix (data-driven container list), surgical (no other behavior changed).
This commit is contained in:
parent
7a5c48c775
commit
b43e6496f5
@ -15,55 +15,84 @@
|
||||
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:
|
||||
'<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/>',
|
||||
bte:
|
||||
'<path d="M12 2l1.8 5.6L19.4 9.4l-4.5 3.3 1.7 5.7L12 15l-4.6 3.4 1.7-5.7L4.6 9.4l5.6-1.8L12 2z"/>',
|
||||
};
|
||||
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 (
|
||||
'<svg width="' + size + '" height="' + size + '" viewBox="0 0 24 24" ' +
|
||||
'fill="none" stroke="currentColor" stroke-width="' + stroke + '" ' +
|
||||
'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
|
||||
iconPath + "</svg>"
|
||||
);
|
||||
}
|
||||
|
||||
// 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 = [
|
||||
{
|
||||
id: "adwright",
|
||||
label: "Adwright",
|
||||
tooltip: "Adwright — marketing intelligence",
|
||||
// Bullseye / target icon — marketing focus
|
||||
svg: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>',
|
||||
selector: ".rail",
|
||||
btnClass: "rail-btn nav-tab has-tooltip svrnty-nav-tab",
|
||||
size: 20,
|
||||
stroke: 1.5,
|
||||
tooltipMod: "",
|
||||
},
|
||||
{
|
||||
id: "bte",
|
||||
label: "BTE",
|
||||
tooltip: "BTE — brand creative studio",
|
||||
// Palette/sparkle icon — creative
|
||||
svg: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2l1.8 5.6L19.4 9.4l-4.5 3.3 1.7 5.7L12 15l-4.6 3.4 1.7-5.7L4.6 9.4l5.6-1.8L12 2z"/></svg>',
|
||||
selector: ".sidebar-nav",
|
||||
btnClass: "nav-tab has-tooltip has-tooltip--bottom svrnty-nav-tab",
|
||||
size: 18,
|
||||
stroke: 2,
|
||||
tooltipMod: "--bottom",
|
||||
},
|
||||
];
|
||||
|
||||
function _injectButtons() {
|
||||
const nav = document.querySelector(".sidebar-nav");
|
||||
if (!nav) {
|
||||
LOG("_injectButtons: .sidebar-nav not found yet");
|
||||
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;
|
||||
}
|
||||
let added = 0;
|
||||
TABS.forEach((t) => {
|
||||
if (nav.querySelector('[data-panel="' + t.id + '"]')) return;
|
||||
try {
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "nav-tab has-tooltip has-tooltip--bottom svrnty-nav-tab";
|
||||
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 = t.svg;
|
||||
btn.addEventListener("click", () => {
|
||||
LOG("button clicked:", t.id);
|
||||
if (typeof window.switchPanel === "function") {
|
||||
window.switchPanel(t.id, { fromRailClick: true });
|
||||
} else {
|
||||
ERR("switchPanel is not a function — cannot route click");
|
||||
}
|
||||
});
|
||||
nav.appendChild(btn);
|
||||
added++;
|
||||
} catch (e) {
|
||||
ERR("failed to inject button", t.id, e);
|
||||
}
|
||||
});
|
||||
LOG("_injectButtons: added", added, "of", TABS.length, "(nav children:", nav.children.length, ")");
|
||||
LOG("_injectButtons: added", totalAdded, "buttons across containers");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -119,12 +148,14 @@
|
||||
_init();
|
||||
}
|
||||
|
||||
// Re-inject buttons if something re-renders the sidebar (defensive).
|
||||
// Re-inject buttons if something re-renders rail or sidebar-nav (defensive).
|
||||
const obs = new MutationObserver(() => {
|
||||
const nav = document.querySelector(".sidebar-nav");
|
||||
if (!nav) return;
|
||||
const missing = TABS.some((t) => !nav.querySelector('[data-panel="' + t.id + '"]'));
|
||||
if (missing) _injectButtons();
|
||||
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 });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user