From 02409a03653603d06d44b72177913f68b0a2f38b Mon Sep 17 00:00:00 2001 From: teahubxyz Date: Wed, 21 Oct 2026 19:34:30 +0000 Subject: [PATCH] =?UTF-8?q?havre:=20update=20pill=20=E2=80=94=20notice=20a?= =?UTF-8?q?=20new=20version=20and=20offer=20a=20reload=20(as=20gandora=20d?= =?UTF-8?q?oes)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ventures/havre/update.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 ventures/havre/update.js diff --git a/ventures/havre/update.js b/ventures/havre/update.js new file mode 100644 index 0000000..5309d00 --- /dev/null +++ b/ventures/havre/update.js @@ -0,0 +1,37 @@ +// update.js: an installed Havre has no address bar to refresh from, so the page notices when a new version has been +// published and offers a reload, like gandora's pill. It fingerprints the files the app is made of when it opens, +// checks again whenever it comes back to the foreground (and every ten minutes), and shows the pill when they differ. +// Checking fetches with cache: "reload", which also refreshes the browser's copies, so the reload gets the new files. +(() => { + const FILES = ["./", "app.js", "style.css", "workflows.js", "abilities.js", "card.js", "venice.js", "memory.js", + "threads.js", "media.js", "backup.js", "theme.js"]; + let base = null, shown = false, busy = false; + async function print() { + const parts = await Promise.all(FILES.map((f) => fetch(f, { cache: "reload" }).then((r) => (r.ok ? r.text() : "")).catch(() => null))); + if (parts.some((p) => p === null)) return null; // offline: no verdict + const d = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(parts.join("\u0000"))); + return Array.from(new Uint8Array(d), (b) => b.toString(16).padStart(2, "0")).join(""); + } + function show() { + if (shown) return; + shown = true; + const b = document.createElement("button"); + b.id = "upd"; b.type = "button"; b.textContent = "NEW VERSION ยท RELOAD"; + b.addEventListener("click", () => location.reload()); + document.body.append(b); + } + async function check() { + if (busy || shown || !crypto.subtle) return; + busy = true; + try { + const now = await print(); + if (!now) return; + if (!base) base = now; + else if (now !== base) show(); + } finally { busy = false; } + } + if (location.protocol !== "https:") return; + setTimeout(check, 3000); + setInterval(check, 10 * 60 * 1000); + document.addEventListener("visibilitychange", () => { if (document.visibilityState === "visible") check(); }); +})();