From 48c4c0e6f14728202275bdea99d2b76939edcabb Mon Sep 17 00:00:00 2001 From: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com> Date: Mon, 12 Oct 2026 09:34:26 -0400 Subject: [PATCH] Serve the MealQuest favicon instead of the browser default (Maestro 7c778111) (#213) Co-authored-by: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com> --- web/index.html | 3 ++ web/public/favicon.ico | Bin 0 -> 9275 bytes web/public/icons/favicon-16.png | Bin 0 -> 785 bytes web/public/icons/favicon-32.png | Bin 0 -> 2707 bytes web/scripts/generate-icons/main.go | 64 +++++++++++++++++++++++++++-- web/src/pwa.test.ts | 46 +++++++++++++++++++-- web/vite.config.ts | 7 +++- 7 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 web/public/favicon.ico create mode 100644 web/public/icons/favicon-16.png create mode 100644 web/public/icons/favicon-32.png diff --git a/web/index.html b/web/index.html index b6b78f5..958c97a 100644 --- a/web/index.html +++ b/web/index.html @@ -4,6 +4,9 @@ + + + MealQuest diff --git a/web/public/favicon.ico b/web/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..08f273b7c2af383a6dc85fd4a25c15808c4a0b2e GIT binary patch literal 9275 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 A0{{R3 literal 0 HcmV?d00001 diff --git a/web/public/icons/favicon-16.png b/web/public/icons/favicon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..7e14a13f9a0a1f21e44ede037b98e477625ca76d GIT binary patch literal 785 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 { + const entry = 6 + 16 * i + const offset = view.getUint32(entry + 12, true) + return bytes.subarray(offset, offset + view.getUint32(entry + 8, true)) + }) +} + describe('production build', () => { let server: PreviewServer let origin: string @@ -94,13 +111,25 @@ describe('production build', () => { expect(pngSize(bytes)).toEqual({ width: size, height: size }) }) + it('ships /favicon.ico holding the logo as PNGs at every favicon size', async () => { + const bytes = new Uint8Array(await (await get('/favicon.ico')).arrayBuffer()) + expect(icoImages(bytes).map((png) => pngSize(png))).toEqual( + faviconSizes.map((size) => ({ width: size, height: size })), + ) + }) + it('emits sw.js with the precache list injected', async () => { const sw = await (await get('/sw.js')).text() expect(sw).not.toContain('self.__WB_MANIFEST') expect(sw).toContain('"url":"index.html"') - for (const { path } of icons.slice(0, 3)) { - expect(sw).toContain(`"url":"${path.slice(1)}"`) + }) + + it('precaches every icon and favicon so the offline shell keeps them', async () => { + const urls = precacheEntries(await (await get('/sw.js')).text()).map((entry) => entry.url) + for (const path of [...icons.map((icon) => icon.path), '/favicon.ico']) { + expect(urls).toContain(path.slice(1)) } + expect(new Set(urls).size, 'precache URLs are unique').toBe(urls.length) }) it('precaches the shell, JS, CSS and every self-hosted font', async () => { @@ -129,6 +158,17 @@ describe('production build', () => { expect(html).toContain('') }) + it('links the favicons from index.html', async () => { + const html = await (await get('/')).text() + expect(html).toContain('') + expect(html).toContain( + '', + ) + expect(html).toContain( + '', + ) + }) + it('registers /sw.js at scope / from index.html', async () => { const html = await (await get('/')).text() expect(html).toContain('') diff --git a/web/vite.config.ts b/web/vite.config.ts index 48a76c4..8d11f62 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -17,8 +17,11 @@ export default defineConfig({ srcDir: 'src', filename: 'sw.ts', injectRegister: 'script', - // Precache the shell, JS, CSS and the self-hosted fonts. - injectManifest: { globPatterns: ['**/*.{html,js,css,woff2}'] }, + // Precache the shell, JS, CSS, the self-hosted fonts and every icon + // and favicon. The glob already covers the manifest icons, so the + // plugin does not add them a second time. + injectManifest: { globPatterns: ['**/*.{html,js,css,woff2,png,ico}'] }, + includeManifestIcons: false, // The web manifest lives in manifest.json: its theme and background // colours are --color-accent and --color-bg from src/styles/tokens.css, // and hex colours may not be written in TypeScript.