From bcc9e5674989f5450725ec32c94dc35a2e21cfda Mon Sep 17 00:00:00 2001 From: dev_agent Date: Mon, 12 Oct 2026 09:23:21 -0400 Subject: [PATCH] Serve the MealQuest favicon instead of the browser default (Maestro 7c778111) index.html linked only the apple-touch-icon, so browsers fell back to a default favicon. generate-icons now also writes favicon.ico (16/32/48 PNG-in-ICO) and 16/32 px PNG favicons from the v1 logo; index.html links them, and the service worker precaches every icon and favicon so the offline shell keeps them (the glob covers the manifest icons, so the plugin no longer injects them a second time). pwa.test.ts asserts the build ships the favicons at the right sizes, links them from index.html, and precaches each icon exactly once. Task: 7c778111-c615-4437-a3f6-0e36bcb97e63 Co-Authored-By: Claude Opus 5.5 --- 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 zcmai4Ra6vEw;ni2I)=#>5`NXhmah)Lvk1x z;Ck=>av$zm_vP&MoxRq6IBS3Fd^-RDEC3Dw1OoiCOaL+*0N@1x0AOPJ4^AQk02Ke- z2?+cLTTlT23nTzQl*SuH62hndyZ{nqC79Mf`!50j1jPP#e*R+jF9!A>WthB<&jQ8o z#W%EybRnl14`YXXjm_{mk=2*jSc%w9+J+HD__1=;(_EcSE`ErXvguFuuZuSev~Z*3 zonhiQUP z7;z|Q`_cMlQJVlnMf9u@m1-G?IqF@j%*knuxUD4ZrX5e%f939TVYZ47xRy}KFcvzr zm8?b}*hI@x1pNc^!Wy*N$Ss-{$~x@kQ-%7(`@)$+#NKon-mgwx?)PfmPSrY?cFTI% zzK9x_7)%PMp|)L7mAUmr7alO`u*e4apP&(?TEt7 z%%#Fs3f6>!&7Q*kI4NOvat8=5Fx+LkD}?S5t~qdq3&urQgj=g}Dn%tH0xeYJukTh~-I@H$?uQ4|sbNrjUCrZv z0HFNp4Xg%g9`=6_g6W@|>3;};0Us3t0ECABAp~pcdq=t*-yx(!O-|>?@)jhK`Biip ztBR$ya=cT$3Z(`F|q!8|>q_I%=e-8c8);eq8q_Q64w_35=CkGu{&6bJ|b;oxp5 zL0w1RhfvZ3ALBZ_#e#B>Y10&EYsyOl1OXsOB!!9VMEMX+;hQXchQeukFzyrOxKgq& zkp&PQtVpp-vcWY@W_dcwv7yAU$XLO7pL*3mQTQo#Ayy%S7jNV{Ai!1zL=0eh?*<_I zLVw9UN}h&_=GA{OMrC3=VgGRsXp!F~N4 zME9Byh6@CceKdd)9)4R?XOzJp6Nm;BQp*YrGbgqmZ{m2}zk@kH=NKZ;O9(9O4m zpM^SLqxa_KeEDoGXndjKkZ1-hRx^HqN|Z1$F@(S3%{Yo0w957iLj!;SfEVw!IIe|q zJZoJMb~vOD_@ok%(S3T~_d%`9tV?R-YA&ev@T!ShsF1}YF&ryg*qj^PhgmN6dUsyE zx2My3a8<2{pK=8c{@WZ*c8(JX3}-$h{PGtW^gvRe&_BTh#G(MncHM4uV>-Um#+r%q zdU?BuK8~CEdN{+qQ)gGjz7v4KMv2(qg+xfY5b~*pvB0s19?C+QX50zCY^)cUN8A3kx(V8QK_!Cag_WtP2 z*QKPmg4ra#2zBCOv%D-!FUoWe(a~wC83$J+U;Zpk6q8^$PKAx}$pr0Jee-U6-k=Mg zP19pylXKl(1VA~-i5@XQQ$9j3dCo@=PS^vmHm&7gH>YEs{^(SdockluT9aGC&HK%Z zi|LCZ=A_oNQp*Zg-t)sB^`hNDYsza8#aRqnM%u5R5hpM^>#m0(1t(!4GuD3S*qrv1$1(b5#BOfd7gtV~h4?4%`X zr}XGf)@#R}^WG3tbA48~*tN_CfRf3Kq4pNGP1!0R{_-p0ZM2#N9hPNE_&Te`u%+hJ zfJuaMN@!mbrEXA1KO^qBzP{d@KTl*XkGt93{hQJ%wDGR%(lXeg(Z}9T*pVQ-q2!4& z$xKX?arq3Rw68O5arMCOk8)kK;bA|1|D7)1*mx3l({lgNuPz&$?{N0x#|XDsHyelw zK)5f58~77*fq3=PYk|+u7=|YO(J`MYXg#Id+J7 zI(|aOuM*1{h!TqwP^b$yXdl9U#9aj(F40^L{X3 zHeLM8Qm-MCe2KC(%gXtV2D7`%RLC0fCku6U=I*4vF22*+TPtjmy}5HZN=09sHgdKg zj`x3FUJ`Cg87<4CzJ6?n^UGR@ntZvHflq+-`KAR4G#Lm31A4?`cE4nlkL|}C*}tYa z8o%35Dn%4ZFBTgXy4N)~!B5vCJk`}3BpWGl9k|h5xfSnqewXi7OQN3)#h*6N#)-dV zSFgw%45TJ|8$Lk@)YM@f0kcpjeDJidFuODNXadu#?qGLc)t;f0BbHoj0%_w*7q8Ogoaif(qFayArOLbLTnwViQHnu%h;=Z6d=2;`g(HCAw0ys zf06Mw(m9l~%BxOUxgPWs_(jjDq6&4P2#sCi7ED z$+EY=#ku;=Hag7y9eZi%^RX5(T<1latfyh1M@|lP>~7vXgB{-UGr=f-SeFcq##$CAt2iQ*9@o&q1@ z>#=w)zRYkVL>tUszHWF3ZtB3% zN@2g7rSifaGCB*)xXZ-l=JP4*vzgk?*_=J9HG^w1cV@uU^x?o(0QwXWAj<(}>-0o( z@7R{A$CGkAUyH>8|9%>_=C3=&`Qc4z;NIE%FO70ixbuBs75j`51#?K$D1Zvk^Q-q| zBguOlW+pNDujt8#GrX<8moC9Ef)iM4!mDG7-2`}4;pwkfV{r>*&gq66wI(6cO;J@h ze4C-vTEczcBo_Cu&)VTpKq8>JD{k-b58*G?bImlD^;HD>IFaDuwIKlE@hA9W)X=$8 z68g=m?~e+7agt<%Ol`Aus4ErQ)R*~>%IpqfE%gO=cwP+|$*W5aw4t_)Q#OC(dJTM{ z!&m93)*&{HJ@D}twuf_A|1C+CW6L4kd5ItYA4&SJiUSDzk0kwL?)(b?pyK$CB<1n- zbu^jFS(!h+((8&)XJurZQ;aHNbx!2?*}(I*zRD+qXF`m=XhM~ii8mUXL@M1|V~mlv zO;u9JZVGDwt8ENWo%B9QIM;@pJ^sm1!!skIXDaJl$CBvb7Jj(wYc~d*u$}>ylfWI! z1nM>ji85)p2<|Sl%RFhDZmO@a3ymzmXTk-@g@F2Cu1sROI2MtDpfEHa47XAO7Vb-R zPA3AccVx7%v`ZHpEhiP*iDixzEBq&}A)jUC2nKNhVVcOAm#P7a>s=&R)W$%-nAQ}W zx38R^f;_U>m5Mp@>n;RHCIqQkV`1{Ce)`@%r-;gr5n zl#!qiLOst3@F16tXR8;}LD&bu(e#b)H6~6vKeb7B{B9ReFJ@P6?-Tfp!3208`~<9b zCA2~Sa(odUFi$Kpkg?3$<0L{fk0&NhV+b1{Jr=OY1_kWTN88AuZDVzDhNU4B#=tsr zouD^5k-Fgmu;FH&Qq4LpMM)#0EXje0lt8}g;*lzo+VQ}Pk^OZA7k!@P40{*#h@_k; z>#Q2u=_Wupr~&|G0#L*X5?arc+wqYNF9=la+cPP#t^;5OFT+E&MSg7hoXQD=)~#Yg zVL(BrZqM(wsaJVp&P#sDW=of$i9EVS_p7w!t{l5IG3BDo)!E{t9CISj*L6*KBKp?_M0$dmn zA~q0Cw-s~iK;+TL%_#Ma^k~+6SGCIIO2?%nNU(C@Qf0=nvokX#&UeO}gvI(4pHY`_ zsZ|!ebT&MoO+PO*Vx4_cv#JV&BoLI1Ip{rxa8%~YM~bgC`jB3cwfM1Fo&D;g{`*$RNTcZ2H#J!bR=pE$jD?wkR2~78>tz zm*cR!2YdaaZX6Hr3#e%C1$TRYJ28D`_Hez?wK8p_&)#ltk#l_u@wl76nbniYwNr+B zU)NZRO_WIqRJr!n7Z4BwYBrD$%W2c*27*wKa)_t}D`zYykza3?0f|O-8{Gc2n3{Rh z<=W*h&>Voe7na`~EDlBbIynY7?!pu$rdDX2oDzy9)=v$j7-Z&?o=VQxmsz*3-mR~w z+Aw9tH#_od%KCoe2NTeGJCqn9gnMcFa5?cXYDN%9_#(%K?DZ^nVl)$Z$V(2{6yj;? zSX=cHnKlvBo{f=|^b7B{(hp-&zHOj;PBLpNTQusUQg(<AWbs8$cmF=x+>M+ds&wlpNf`7X}2zJ)CZJ|)MAoGr=g;1%hTLJk7_+;*8(x+< zWj0z*Z32OO@E5Hs7>$+-p;Vg%kzt*lCHbEOcidN{65#ezt4GZYF=`IUENAUH%fGy1 zzmY89GIz*oHJ39nHCx0rmR#XjTdQa_#hv!ifyB%%R8C3#u^B^`d!S{J5!SS$!QK4fufOKiI(F{~ zf=~DNX3G}r(m!G+xT6mybJ|WLh(bOLNxKlP5Ceb#Kb<4R^cED;4mU6wHbwGXc@{4$sK@3WwF{Y5%~SY^sLd5>_Tvv% z%#n}yFw-EBE2EUO()}AQ#`IDMT2WdX7yaA6Ka}7u`nD4Fbs6}P6H88Fmp+9xDUW01 zREWba1(GXb=4ZkjM1Z9XwiGNR%*0V2>-K$X-f~tmMrBqHM_C=8P#)FVl1DR$2?=_; z8lZooa^v|KI3r)GPMD_GjY;G`K9~L}%z|~Z(T%3Tb-m61z4EmEepHv2JfdrUF30b} zu{*VzQO_tqR;Ro@PDK~Ukxpt2w*s`9Q$S$gl;4U#h;3IvCE*LT^C^r>-{BGMOPu*$2Dm=TvbOj6oOQJPmdo)nl_| z#9#PAK(96DD7xrl@C?bLzIX((e~O#;?dsAtHC$;`poi9-{M=(J3VL2<+k2<}pd;7^ zkSnl{Cd)Ni%FYTbAy40n{0NmN)QQU{4e~ls&6I+pnIE$WPD!_~O>>KI3VNN-|!(4{~f|AXLDK zz~k0;d}A^Z&>H~R6vkcRXFS$bL$#i>2L}c_y<n^*HjyPFkG`4*sgH#SqB;`gjZK!#&9xT zf>5Pjf-7h_<>ZX~T35)`Hk(cL4f9>{xDwU#ZiW*^^zEQ_?vD?4bPARN1xg7$oZM=@ zbw2`ALIozTsxz1E91khpcnuYPePZzoY4c6(qA5punYRuLkZO-_IT9Bus%V(RL30_p z8VQ1k4VJNZSeCU9XbhLDmTmq}z)PH*bsuo26iB#@HyP(2mzG{aR@2c3xZh4~{#mVb z>A(!q&ON2d3X)=kZV!(Bo*0$#P~%H;u61EJWYn9;-4CyM{tMz+JexAL*!~+}&e;l* zhd~VGp0boe&|kn7Oc6` zBsor;Rn_>fyx<+DTUqFe4V=qrfEPNn~X3}E}o&#dkF>DmyfFmXY>oilu{e4Crf zU!p}?P|%KEtgUq1Nk=O~NsLD28{0F`SGx)BdB7OFh(J>BOa8nXE(`9n82m6lhqk~1 zBQZWg^0R|m4EppwMB)A?>bUA8t<%qo-;7)MnkebzEqb)mpOkXdKR$5hDo{?Txzws~ z)#Zrlk(W%4VKB<2q)lQShrYgF#OT>)!gu-VClbC|y&&Mndgx~Dg=D&&yG_S=JO3j~ z_I;8AMcM23N4o&-ijqoP9BDg>7pd_o-*nEU6q|TZgT>c{XydPIM)B=kMtdi*CV@dk zyae2mNw-0LNjz*8)xw@8;l+>eLGRh~M zCCt9Kx&Sd_!BeYg(m$E!&?(&+2#4gwAI$MzS>TSjuZTEg*ZaUqZJPAPZfhzz|6Xn6 zn7!FaT^Cy5#!h1i5PKx7*7A%)1DX5CVYOe!k)*=SG5%U-9YM(Y@m(Y z^^1F3WRMz@mzVc;%E(v@f4tUQhTr$PQ^&0dTg3cV$4*oBAd50}vw6PpS{B_Vc08+! z5+mXiC#qk-EpuO|lOItqCi6e>Kf%EQjUtU!x=vFW(Wc$$foDIm^YjfDjv8%4mc@oI ziXuWD4!XPB8|rlUQWZW))~ebVefZ^x{`2GQWCa3dQ42eOBvPThAfRtC+mwmuG$SACIo#$msa6Ya9MK3_nWgG7NY3At>r zCZ0tPN0q&}wAG**g^;GUE?Twlmsky5ZGQVYX?th=#%|u?b9v6EPvdg$8{2B;Ddox> z{6534P%hF7UbJ4SIj{Rq?}gsh3~7xF|1t1J{c01*34SM>r8MYsvA8C@4B2F9$R07-`|bpVGytHmU&s}rdTMf5@(aw8mG%s_;U_6umwUnF00Qp z-^1b2dd<;nR!fvV*7J*{z-I^g1N@!swlcpnJWhP|(*#E`3EQ-o{me!Z3r2Y4_ zM=`%um-;tzF-uR9+!e_`N%pi{h3rY_^}Yx6YQE%?hk@lnebX@+<6-`qx%+#U1j8Di z=C{O(R{H#x_Pw)h13PI4`jaS}_^>a-o38dZr>5%MZ&)p}c0#>Wh#C6MqQ%ND^O&24h+r5*OztkhHk(d0dui*q7Vp z2&C`rsGH-5x&qozb=jOusVwhMP50>#Jlj;RZdA(s{FbEzfhVO|rqaWOzEIkvS9J~j zpOQ|DV~d^xV4q(``f!gZ=wb@PAKdo0SoBkdEj@j5!sF;u>u;L%HQnA2Fo74zw}PuEg8SvQv#=MRXHb{(!~fN3HH}- zA|~|tQPcKuchz2t_{l7J1cPF@bn?4m&Wblb_DA(t$$v4I&kto$PqGWqG&%c~HrC!vXT?TnSw8>4Xd@pJp#>7>ZWI?ZLnGU>>S<|iwO1x)N(?Jq4bEB3PUkHj@j%! zzNv3ZnwS5kJwy>7zIp3ZL&L~)b?md`i9LaUL4zr>7vEQb*m!-t`G!uK*m&^~kzbU_ zUmZEByCR+a<_T1}PrFP1!%Pai8KR2VN14hJd7|2(MM1oM+ z<&(!XM^tH7`~4ZidQ%6g5tnkVZjAvY{59`k>;(od1Qp`9I5M^1UrU&V&R@#++iKN6 zA52S|3;SrZ7{pPeuR_^_@qyWflyNkx>nJVWFz+XMTZCT3+ zd8)~MukX6FJ;7wCXM9Z@it#Rxj3L7g)0~8;@xrj6wcXyYG`bDT--oWQ4)0AI$LhF& z_7nivU-tHKL9TpniLn3HPGn>7kvz+JD_wW4#UxHSvde7^`s{wIt*_@_Q>(}2(b}ms zYm7#ihj~l~JnCsp9vcTdxv=%`M#t&TQK!-URQ<-*?Rv-j&e(O2a&30ml7iltG zD&ocDhvRPks+$HPutv6N!TJiT!E)V=F@0a%_3sE`k 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.