feat(kez-chat/web): SW auto-reload on deploy + visible build sha in footer
Two related changes — both aimed at "can you tell what's deployed?"
1. SW auto-update (no more "refresh twice")
The default vite-plugin-pwa autoUpdate behavior was: new SW
downloads on first reload, activates on second reload. Users
refresh after a deploy, still see old bundle, get confused.
Now:
• workbox: skipWaiting + clientsClaim → new SW activates and
takes control of existing pages immediately on install.
• main.ts listens for `controllerchange` and calls reload() once.
New SW takes over → page reloads → new bundle loads.
Net: deploys land on the FIRST refresh after the new bundle is
reachable. (Caveat: the SW that's currently running has to
download the new SW first, so the very first refresh after a
deploy may serve stale + then auto-reload a beat later.)
2. Visible build sha in the footer
vite.config.ts now runs `git rev-parse --short HEAD` at build
time and injects __BUILD_SHA__ + __BUILD_TIME__ via Vite's
`define`. App.svelte's footer renders the sha as a small monospace
chip linking to the commit on gitea, with the build time on
hover.
"kez-chat web v0.1" → "kez-chat [abc1234] · source"
So when you refresh and the chip changes value, you know the new
build landed. When it doesn't, you know the SW is still serving
the old bundle.
3. Killed the `apple-mobile-web-app-capable` deprecation warning by
adding the standard `mobile-web-app-capable` next to it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
ea139641e3
commit
d789e872b1
@ -13,6 +13,9 @@
|
||||
|
||||
<!-- iOS Add-to-Home-Screen icon + status-bar styling -->
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png" />
|
||||
<!-- mobile-web-app-capable is the standard; apple-mobile-web-app-capable
|
||||
is the legacy iOS-only variant (Chrome flags it as deprecated). -->
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="kez-chat" />
|
||||
|
||||
@ -66,8 +66,18 @@
|
||||
</main>
|
||||
|
||||
<footer class="border-t border-gray-200 bg-white mt-16">
|
||||
<div class="max-w-3xl mx-auto px-6 py-4 text-xs text-gray-500">
|
||||
kez-chat web v0.1 ·
|
||||
<div class="max-w-3xl mx-auto px-6 py-4 text-xs text-gray-500 flex items-center gap-2 flex-wrap">
|
||||
<span>kez-chat web</span>
|
||||
<a
|
||||
href={`https://git.ptud.biz/DukeInc/Kez/commit/${__BUILD_SHA__}`}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="font-mono px-1.5 py-0.5 rounded bg-gray-100 text-gray-700 hover:bg-gray-200 no-underline"
|
||||
title={`built ${__BUILD_TIME__}`}
|
||||
>
|
||||
{__BUILD_SHA__}
|
||||
</a>
|
||||
<span>·</span>
|
||||
<a
|
||||
href="https://git.ptud.biz/DukeInc/Kez"
|
||||
target="_blank"
|
||||
|
||||
4
kez-chat/web/src/app.d.ts
vendored
4
kez-chat/web/src/app.d.ts
vendored
@ -8,3 +8,7 @@ interface ImportMetaEnv {
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
||||
// Vite `define` injects these at build time (see vite.config.ts).
|
||||
declare const __BUILD_SHA__: string;
|
||||
declare const __BUILD_TIME__: string;
|
||||
|
||||
@ -6,4 +6,22 @@ const app = mount(App, {
|
||||
target: document.getElementById("app")!,
|
||||
});
|
||||
|
||||
// Force-reload when a freshly-installed service worker takes over.
|
||||
//
|
||||
// Default vite-plugin-pwa behavior is "next reload picks up the new
|
||||
// bundle" — fine, but means users have to refresh twice after a deploy.
|
||||
// With workbox skipWaiting + clientsClaim set (see vite.config.ts), the
|
||||
// new SW activates immediately and emits `controllerchange`; reloading
|
||||
// here means deploys land in users' browsers on the first refresh after
|
||||
// the SW finishes downloading. The `refreshing` guard avoids reload
|
||||
// loops if something weird happens.
|
||||
if ("serviceWorker" in navigator) {
|
||||
let refreshing = false;
|
||||
navigator.serviceWorker.addEventListener("controllerchange", () => {
|
||||
if (refreshing) return;
|
||||
refreshing = true;
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
export default app;
|
||||
|
||||
@ -1,9 +1,26 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import { defineConfig } from "vite";
|
||||
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
|
||||
// Build-time identity: short git SHA + ISO date. Surfaced in the footer
|
||||
// as "kez-chat <sha>" so users can eyeball which build they're on (handy
|
||||
// when verifying that a deploy actually landed instead of being cached).
|
||||
const BUILD_SHA = (() => {
|
||||
try {
|
||||
return execSync("git rev-parse --short HEAD").toString().trim();
|
||||
} catch {
|
||||
return "dev";
|
||||
}
|
||||
})();
|
||||
const BUILD_TIME = new Date().toISOString();
|
||||
|
||||
export default defineConfig({
|
||||
define: {
|
||||
__BUILD_SHA__: JSON.stringify(BUILD_SHA),
|
||||
__BUILD_TIME__: JSON.stringify(BUILD_TIME),
|
||||
},
|
||||
plugins: [
|
||||
svelte(),
|
||||
tailwindcss(),
|
||||
@ -37,6 +54,12 @@ export default defineConfig({
|
||||
],
|
||||
},
|
||||
workbox: {
|
||||
// Activate new SW immediately + take control of existing pages
|
||||
// without waiting for them to close. Paired with the
|
||||
// controllerchange reload in main.ts, this means deploys land
|
||||
// on the first refresh instead of the second.
|
||||
skipWaiting: true,
|
||||
clientsClaim: true,
|
||||
// Precache the SPA shell. Chat data is fetched live from /v1/*
|
||||
// and we DON'T want it cached — see runtimeCaching below.
|
||||
globPatterns: ["**/*.{js,css,html,svg,png,ico,wasm}"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user