From ae900b82caf57207b0a58c7ec90dea805e8114df Mon Sep 17 00:00:00 2001 From: rays Date: Mon, 28 Sep 2026 14:11:03 +0000 Subject: [PATCH] Name the icons by their contents in the pages (#57) /favicon.png, /apple-touch-icon.png and /logo.svg are kept a day under fixed names, so after the new logo went out, curl through the tunnel and browsers still got the old one. The pages now ask for them as /favicon.png?v=, the way they already ask for app.js and app.css, so a changed icon is a new URL for every cache on the way. /favicon.ico cannot carry a query, as browsers ask for it on their own; it keeps the day. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + tests/ui/app.spec.js | 2 +- web/build.mjs | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0076ef0..fa6c306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- A new tab or home-screen icon shows up as soon as it is deployed, instead of a day later. - Opened from the home screen on an iPhone, the top bar, the reader's back button and the top of the feed list keep clear of the Dynamic Island and the status bar instead of sitting under them. diff --git a/tests/ui/app.spec.js b/tests/ui/app.spec.js index 707b55e..be974c4 100644 --- a/tests/ui/app.spec.js +++ b/tests/ui/app.spec.js @@ -1179,7 +1179,7 @@ test('a file not yet downloaded has its icon in line with the rest of its row', }); test('the favicon is the logo, square, from both pages', async ({ page }) => { - await expect(page.locator('link[rel="icon"]')).toHaveAttribute('href', '/favicon.png'); + await expect(page.locator('link[rel="icon"]')).toHaveAttribute('href', /^\/favicon\.png\?v=[0-9a-f]{12}$/); // A browser asks for /favicon.ico on its own, signed in or not. for (const path of ['/favicon.ico', '/favicon.png', '/apple-touch-icon.png']) { const r = await page.request.get(path, { headers: { cookie: '' } }); diff --git a/web/build.mjs b/web/build.mjs index 957f11d..6542693 100644 --- a/web/build.mjs +++ b/web/build.mjs @@ -72,6 +72,11 @@ export function buildPage(name, { minify = true } = {}) { let out = page.replace(marker, ``); out = out.replace(//, () => ``); + // The icons are named by their contents too. They are kept a day under a fixed name, and a new + // logo went unseen for that day, in browsers and at Cloudflare's edge. The server ignores the + // query; /favicon.ico, which a browser asks for on its own, cannot carry one. + out = out.replace(/(href|src)="\/(favicon\.png|apple-touch-icon\.png|logo\.svg)"/g, + (_, attr, file) => `${attr}="/${file}?v=${hash(fs.readFileSync(path.join(here, file)))}"`); if (!minify) return { html: out, js, script }; const r = html.minifySync(out, { minifyJs: false, minifyCss: true, removeComments: true }); const bad = (r.errors || []).filter(e => e.level === 'Error');