phonograph/static/dev_reloader.mjs

92 lines
2.7 KiB
JavaScript
Raw Permalink Normal View History

2025-10-01 22:36:19 -07:00
// This used to be based on waiting for a websocket to disconnect, but that was
// flaky. Now we simply poll the shit out of the healthcheck endpoint.
export function initDevReloader(healthzAddr, pollIntervalMs = 500) {
// State model is implemented with variables and closures.
let auto = !!sessionStorage.getItem("__dev_reloader_auto");
let connected = false;
let initialized = false;
2025-10-01 22:36:19 -07:00
let interval;
const button = document.createElement("button");
const indicator = document.createElement("div");
const label = document.createElement("div");
// Rolling our own reactivity: call this after state change to initialize or
// update DOM view.
function render() {
button.style.appearance = "none";
button.style.background = "#fff";
button.style.border = "solid 1px #0002";
button.style.borderRadius = "999px";
button.style.padding = "1rem";
button.style.position = "fixed";
button.style.zIndex = "999";
button.style.bottom = "1rem";
button.style.left = "1rem";
button.style.opacity = "0.5";
button.style.boxShadow = "0 0.5rem 1rem #0002";
button.style.display = "flex";
button.style.cursor = "pointer";
button.style.alignItems = "center";
button.style.fontFamily = "sans-serif";
indicator.style.width = "8px";
indicator.style.height = "8px";
indicator.style.borderRadius = "999px";
indicator.style.background = connected ? "#06f" : "#f60";
label.style.marginLeft = "1rem";
label.innerText = auto ? "Disable auto-reload" : "Enable auto-reload";
}
function toggleAuto() {
auto = !auto;
sessionStorage.setItem("__dev_reloader_auto", auto ? "present" : "");
2025-10-01 22:36:19 -07:00
if (auto && !interval) {
startInterval();
} else if (!auto && interval) {
clearInterval(interval);
interval = undefined;
connected = false;
initialized = false;
}
render();
}
2025-10-01 22:36:19 -07:00
function startInterval() {
interval = setInterval(function () {
fetch(healthzAddr)
.then(function () {
if (!connected) {
console.log("dev-reloader: connected");
if (auto && initialized) {
globalThis.location.reload();
}
connected = true;
initialized = true;
render();
2025-10-01 22:36:19 -07:00
}
})
.catch(function () {
if (connected) {
console.log("dev-reloader: disconnected");
connected = false;
render();
}
});
}, pollIntervalMs);
}
render();
button.setAttribute("type", "button");
button.addEventListener("click", toggleAuto);
button.appendChild(indicator);
button.appendChild(label);
document.body.appendChild(button);
if (auto) {
startInterval();
}
}