128 lines
4.5 KiB
JavaScript
Executable File
128 lines
4.5 KiB
JavaScript
Executable File
// Vestibule content script — production implementation.
|
|
//
|
|
// Single responsibility: inject and manage the hidden menu overlay
|
|
// and the lock overlay. The 3px-from-top gesture is the direct
|
|
// descendant of the 2001 VB6 Form_MouseMove handler (Y <= 3).
|
|
|
|
const EDGE_PX = 3;
|
|
const HIDE_DELAY_MS = 1500;
|
|
|
|
let menuEl = null;
|
|
let hideTimer = null;
|
|
|
|
// ─── Menu construction (data-driven) ──────────────────────────────────
|
|
|
|
const MENU_ACTIONS = [
|
|
{ action: "back", label: "Back", symbol: "\u25C0", handler: () => history.back() },
|
|
{ action: "forward", label: "Forward", symbol: "\u25B6", handler: () => history.forward() },
|
|
{ action: "refresh", label: "Refresh", symbol: "\u21BB", handler: () => location.reload() },
|
|
{ action: "home", label: "Home", symbol: "\u2302", handler: () => browser.runtime.sendMessage({ type: "navigate-home" }).catch(() => {}) },
|
|
{ divider: true },
|
|
{ action: "unlock", label: "Unlock", symbol: "\uD83D\uDD12", handler: openUnlockPopup },
|
|
{ action: "admin", label: "Admin", symbol: "\u2699", handler: () => browser.runtime.sendMessage({ type: "open-admin" }).catch(() => {}) },
|
|
];
|
|
|
|
function buildMenu() {
|
|
const menu = document.createElement("div");
|
|
menu.id = "vestibule-menu";
|
|
menu.setAttribute("role", "toolbar");
|
|
menu.setAttribute("aria-label", "Vestibule controls");
|
|
|
|
MENU_ACTIONS.forEach((item) => {
|
|
if (item.divider) {
|
|
const span = document.createElement("span");
|
|
span.className = "vestibule-divider";
|
|
menu.appendChild(span);
|
|
return;
|
|
}
|
|
const btn = document.createElement("button");
|
|
btn.dataset.action = item.action;
|
|
btn.title = item.label;
|
|
btn.setAttribute("aria-label", item.label);
|
|
btn.textContent = item.symbol;
|
|
btn.addEventListener("click", () => {
|
|
item.handler();
|
|
hideMenu();
|
|
});
|
|
menu.appendChild(btn);
|
|
});
|
|
|
|
menu.addEventListener("mouseenter", () => clearTimeout(hideTimer));
|
|
menu.addEventListener("mouseleave", scheduleHide);
|
|
return menu;
|
|
}
|
|
|
|
function ensureMenu() {
|
|
if (menuEl && document.body.contains(menuEl)) return menuEl;
|
|
menuEl = buildMenu();
|
|
document.documentElement.appendChild(menuEl);
|
|
return menuEl;
|
|
}
|
|
|
|
// ─── Show / hide (step-down) ──────────────────────────────────────────
|
|
|
|
function showMenu() {
|
|
clearTimeout(hideTimer);
|
|
ensureMenu().classList.add("vestibule-visible");
|
|
}
|
|
|
|
function hideMenu() {
|
|
if (menuEl) menuEl.classList.remove("vestibule-visible");
|
|
}
|
|
|
|
function scheduleHide() {
|
|
clearTimeout(hideTimer);
|
|
hideTimer = setTimeout(hideMenu, HIDE_DELAY_MS);
|
|
}
|
|
|
|
// ─── Unlock popup ─────────────────────────────────────────────────────
|
|
|
|
function openUnlockPopup() {
|
|
browser.runtime.sendMessage({ type: "open-unlock-popup" }).catch(() => {});
|
|
}
|
|
|
|
// ─── Lock overlay ─────────────────────────────────────────────────────
|
|
|
|
function showLockOverlay() {
|
|
if (document.getElementById("vestibule-lock-overlay")) return;
|
|
|
|
const overlay = document.createElement("div");
|
|
overlay.id = "vestibule-lock-overlay";
|
|
overlay.innerHTML = `
|
|
<div class="vestibule-lock-card">
|
|
<h1>Session Reset</h1>
|
|
<p>This kiosk has been idle. Your browsing data has been cleared.</p>
|
|
<p>Click Unlock to start a new session.</p>
|
|
<button data-action="unlock">Unlock</button>
|
|
</div>
|
|
`;
|
|
overlay.addEventListener("click", (e) => {
|
|
if (!e.target.closest("button[data-action='unlock']")) return;
|
|
openUnlockPopup();
|
|
});
|
|
document.documentElement.appendChild(overlay);
|
|
}
|
|
|
|
function hideLockOverlay() {
|
|
const overlay = document.getElementById("vestibule-lock-overlay");
|
|
if (overlay) overlay.remove();
|
|
}
|
|
|
|
// ─── Event wiring ─────────────────────────────────────────────────────
|
|
|
|
document.addEventListener("mousemove", (e) => {
|
|
if (e.clientY <= EDGE_PX) showMenu();
|
|
}, { passive: true });
|
|
|
|
const RUNTIME_MESSAGE_HANDLERS = {
|
|
"show-lock-overlay": showLockOverlay,
|
|
"hide-lock-overlay": hideLockOverlay,
|
|
};
|
|
|
|
browser.runtime.onMessage.addListener((msg) => {
|
|
const handler = RUNTIME_MESSAGE_HANDLERS[msg.type];
|
|
if (handler) handler(msg);
|
|
});
|
|
|
|
console.log("[vestibule] content script loaded on", location.href);
|