From 796bac47b412ba523ff0ec056d1de75c89a97e4c Mon Sep 17 00:00:00 2001 From: davidels1 <87242587+davidels1@users.noreply.github.com> Date: Thu, 25 Sep 2025 00:50:36 +0200 Subject: [PATCH] Add icon generation script and new icons for PWA and favicons - Introduced `generate-icons.mjs` script to automate the creation of PWA icons and favicons from the provided SVG logo. - Generated icons include: - PWA icons: 192x192 and 512x512 - Maskable icon with 12% padding: 512x512 - Favicons: 32x32, 16x16 - Apple-touch icon: 180x180 - Added a new PNG file for the maskable icon. --- index.html | 4 + package-lock.json | 4921 ++++++++++++++++++++++++++- package.json | 5 +- public/apple-touch-icon-180x180.png | Bin 0 -> 2529 bytes public/favicon-16x16.png | Bin 0 -> 236 bytes public/favicon-32x32.png | Bin 0 -> 376 bytes public/icons/icon-192x192.png | Bin 0 -> 2745 bytes public/icons/icon-512x512.png | Bin 0 -> 11518 bytes public/icons/maskable-512x512.png | Bin 0 -> 10655 bytes scripts/generate-icons.mjs | 59 + src/main.tsx | 4 + src/vite-env.d.ts | 1 + vite.config.ts | 35 +- 13 files changed, 5024 insertions(+), 5 deletions(-) create mode 100644 public/apple-touch-icon-180x180.png create mode 100644 public/favicon-16x16.png create mode 100644 public/favicon-32x32.png create mode 100644 public/icons/icon-192x192.png create mode 100644 public/icons/icon-512x512.png create mode 100644 public/icons/maskable-512x512.png create mode 100644 scripts/generate-icons.mjs diff --git a/index.html b/index.html index f6f1258..b653480 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,10 @@
+ + + +M|%itw-9NXOk8@4?KG+OFBh+pr#IFoZ_o z^6Jja1EnHF^u!jNrTqt*t#o^Ob#f5g5fVhW!FT z@aRC~GFvj`K5@w 7+2CVux77Z!% zLf}hbjJz^T=Wu!9~%z8;nq1UJ*e2J$yE()v_q5a+1xe*aH4K()I!=sB_xqW zIJm3x<01_sju9#-D{oB{8DbjQCamNqL6ne`9HHUnT6{MgR{aHoCz=q*#N$EbiQEwE z!72jX05mky8K=~luHn>UgI1Q#Ws9c-y#B7bc0;>wovvM_3(65{t*?Hxvb=a!7b7%V f&1~$(glj<3bD(rYfqGj0X9IZb^LDLr#+~~&@^-gP literal 0 HcmV?d00001 diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..3b5389b3374b232676f2c55fb8e8604b2faf9912 GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9G0FF=@aYjsdIP;j27 zi(^Q|oa6)`Fu2G!-SiS?Z1NK6Zzm(n-`Lyn1PbrE=VBf*mvLRB`5Rru-We8OWIgnm zPo!NkJ _SucI&_Q_g5G!a&pLdLP$otMacSY5KlVi)>Lond0T?BI&bJuCpy WHh99UT|5H-0000 r9^Gf*3uc8C>>1EP*qEniM3S(rM4l~8ft`~wiacI)H-8n zYHFz#MM+33Rkc)WtF;T7+FFznzL; #!^W1yRALritp7T8Kdrp$o#q+{~hXnxu z5H>Y2BJnMAe NSBSMCfGBL{dc9GhD6Qa^+n9(r?@Lr2P7i3v-Oh2TS$!4^EyEnHZ>8 ztgC#5h^_r=cHKMhyM18T$?!;*=3Vn u!AS%cZ6h;d-hU3lF~3 zw|r*OU}203e3-8<*}SV7RP;m7Z9mEKNcQ;Z^ty(Lecei%AmVmP?L>yOZRBFthJWYM zAD3M$gnuqjhO-tN2WxJAoSUoqd(3UMGqJWXdH5R%mTD;sUp9Mqv& 5*b_|!N%=Fi~E;X(mw@Sd7SIk^KB6?uM@e`z7A@Yev0GXQ6en%CCy?mh53JaO5w z6fS@lqD?Pe7+zR3?Uw&J;@ZkapbufiP@VF<(EogNPTbB!V{~iy>3cF@a|lX;!;MM0 zb7t4TIImk$?%SMtf2nilhGpE2)6|}RuKX8Mf2=moWqxGh)}83^`tfn)?r2ps{C7Q( zk!We()OYu-^9mAfq+14c6uq%_m(NI+X)$Ylmb242L!QRpGSJOD%yrGx-j;n&yK(Xy z77S>W652XDu_rPtJ&0HFzsDi#Oqw^Tep!CY& 3n=Y4e*)08Z%Q88b)7Y9<$OjGMsH0IU+Sri~?$e*&m+4of_SpiuvzZdE{lVW? z61u7$*eBLrCm~X4F{y`OQ2}S0h?FqQ7Xy87MR<10sm6!gwcfRuSu)vzyt2{NS#Rg! z2fIPQK0ZV%Se^ %ZG?1bQm^YF(y~bGY#y1|--WK1#zvWoM)$jW82~RhaqK)l3 zM!3J;AF;mo6@!7AL9k$XU#0!-iheXag*Fa? yq!;78%$rTmiJ^V1TigDF$4v>eM#bCiI z(z;I+Arz;LH4#OHC`|6^3PKc$vXWRwe*yZgVvuM!4aCY+gs%v$niliJ@iy&yy>tTt zA0=D8Un~%@xnF?e7~1q_TJp`RK|JX1O`t^lyac4E0>`pWs%txXVAvkN`5W*2rFP2Y zL U}Ql!06Y zbfI;sd9nZmWhOy@5&;;P9wTbrOJoOp1~5p7kRnDZOZLS)L`6YmCQd2mFkP-A2RdLh z$QE+9DpR>olNt N(nT z4amY|Q4GZ{2wNO +_QbKIyvI0XHEl9ERKrGa`1saXO)n5 zMj;sHzxS=sN&@ft&{P19#K9Er(qn=M$Fb3207xJLQYtk=s4SW(YqJ)HqyGPJdmuC- z05hbScJYC-gtD9JWkk&k&DAz+)^R~=)ps&f^&{?AW^k!G5ubvEyhnB5$}Ef*6*y_- z#*j+fyd?`j70AWl5!xe~lpdf6D(3Zr)@NU}W36#?6MDES>bZFQ@wQHW*CgJQsr==rm>E{U@t_8@d=CrOo&GaVf!b> z47cw`{NvNf`+gZQooa+jHBf*gF|`p?@ECLkhZ0Rkre=~&$Q*Y}m7%I3n27Umqtppb zzWgsulH8})Lf%63!5g58fEy++%1WR-pcD-vV5YG#1&ODbDf?{RrySmfHLAgYu~@`G zGDTHzS_O=wl9b?SHqS20zrbN+Q1Hx7qT~;`uXUK`sU#enRbdlcFVk$-tX7VOBVf6H z8!ls^+)3XDUV$ +Xz_(QsL={&{Fr1Waqo(g7mGWy7BfR8-*VqqJbSSEGCb-z z6ldtI4rg(%)z)rTZYKkF9mQd~p;W$NWb!{8YJBgq0Q1x0=P>AoifD=MJ11ae` b44mJzk7Z;ER%kTR(SX_Y i@WsP$gv=rBgWuimB03O)grWg84NifYG|12g9>4A*EW29lxXIu z^A0ju%(~k#wNn8}JMB(H^jl1G6q^q}*BF&o(6ZR{Eh@MNicMxN$hBa5$m**sC3Oc0 zenY&~9$P#02uQaoTc>6BoXTjoU!~f85#aDT2*{Vt^!@G_IQH3ZPl>TEE(8%#yoSo- zS$nRXwiSC5x*f?H@9d5~#&2NxFXW#tsM=Uuo}bTObqX~>9#F)iBuba|is*41^K&wH z6kRB93puQJoa)&y79xPjgENpuCxdaYD!}%7rS3qR@kE> zo1a&IO#m4huD{4N@49c+T|>XA*q&O7;F !s}x-^_8&>I*L z`* 7OyQ4LqH2D1w7tVY{Ps>vm9 ^etdGzu|BVU>v)hm5mn#%a#)Z)Oa?{($$n-s`=(E-rKVp6>5``rh;T+}HNIIcqKX zVF>^LT6=cy_!$6T;71s+NCW&c5z#6H|11vPeIyhB)Cc6hYQ>R12LONxuxH2CgAw;e z?_`C+>lr_~mGZ?L5Bx6Hrw8-qonOXtYb3H$YX RY^YTr)UN<;-&-+}G#cjn&GCTjsBdt?!a 9YG zpua1YFeoKfh#hp#jXw(R=LV{$sv%Q17htXIE@8@A@I5vj9VAiG_+WrYVm_)xd+EbB zKG3m~w#Bb(I(M9S*#iIX=MIs9pIlli?ffYl{UiO7#Ss{O11}wJt!`Klc8qU)karSa zk9N2e^wuw^>uJy1!nWys+m>6>4t2H<-V%CH9_EjtCyVjx{5L;z9H1hC0l3?%=
wJJRw-U{qCdu~B}*_Ji_lqdPveHeb^r_<%{dH|$Fx;p+0E3xBwTu0Zx zdZ&i}N=odHcFOykLLnTw&gTd_j)vDBx65)&RcyV|wYp!V)iJImThvh=I*xCf_S;tC zz-h;G-q;aWxKI*pSs~e iLLXE4~{rQ%Goa zXpH9knW%0TrM`N|$=KMjy0snGed;{>=8(wVboE3txm8Rz7i={aei|wN*b5-fUQx%E zSqY-M^K&CFZt1NC_w&lh*Qd!&PUdvJRqe&>r&i|*oCAuzKZJE6Cts3NeHku}4*nDe ze~|;}zJVi>4ImpoMKiR?8NV6qL*)>Rb#CRwYwykKXJ+>W8b+KGf)d%8Tx1WtoC%CP zbp96!&crpXJ3G>Y?=JXYY>PWL15V%%Qn_!cyH{KhPXiy57$fd3M~#a+^OhTc#s?3$ zm#Tg%$#y5bAvV%3aohQ%@}cXwEhDakJL ~eJ{D(K$3U $o`^nT-r{+Y^d_lBWqw-`jE?WuHJh3Sf@LYZG^51y|KMEUU9Mo+HvO3tKyJJ z{?$gy&BC<9JL>b_E?nRXUOGy>XJ;nfXQoIOW#q-HIW0^z7Z8w!w%&${eanV13ml6M z@k53<|N7X2ATKl`{PG96=QB05rB;6KN&Ds?X@)Ds-^Xm6787xVkV{IO3~A~8lcX^6 zfD`&{)%Ew6Ci!vq&OhIDl8=!T8}+b5xgmgDBW`b^DMjPjL@l=K<>eNj4<;m``lzAo z+0mA08RgKltVmeC%guy#cxD&f({94>l2Wo~!I}92`ks1M>>|dQH@&W?0)frTMT~vg z@{NO!MLMpVxpVbV7e-RKBK++sv+!MK$UV)W1In#S+M_E&W_H_aciZnyzPOag yUXH6?YDC`hNEP`H}mz_mt`fz0KW#!f&(H@qw(g>!JBVZ27@!X}YX0Ogc{!(Wua1 zut~y}nC-Vb+}arQoZT4ylCD9<@!nW45~I}PdYmvFqif%Co{-6!_bDa1TyA^A?7EJ} zxV>ER6LPlZ-E8s~PV-uIrB+{uWm=8?XOe&6!TS_kyf8~m;a+NnSD)Rj9?vB|)x4Q2 zVAx+U+@zfDa~_<2dx$gawc16rW^yM?=~8w&%(XG^kLD4YntX(54E?V4)z!Uh<$Ww3 zt2@WKmpmqy@~7>IgjVM@tH7KGI$JJNwsb3HHG3Q-%^ti(vT|dyL%4EV0&ojMb1d_> zK;4T?N4)EAn~x6j;?!pEpV@Ry15XdyMeop;Jwc&IX!}U%NV(o3Htpx-llLPX@ ;00WJ>YP@P2YouH{66Yk}m>(isEwxt7(OQc~0#J% z9`<`}p3j+AM&^Af!lg?m 02@UoK6QnMugA?f2Q>OJ9~Ndl>aoB zoKrI&{ioh#Y@~nv=By&wn}wWaFwM&~@5JQkBglCM8O5EIIr-Ryy7y+|CrUk#>1+KX z7HjMN@ug@``+5@QGenDlEQE{5WrsAv*6Lc4ECX^keY^b~1~0I?lO+{1jp(^}Z1&v{ zvz?Yc*2a)Mw-S)cA$PV(uLd7@7nBryO6nk@r3( `fxZyNoq z-wvChG)G)FTH4K89e}5u)xg)|ZSXVNXm`C0XxdH=TXo9=mH>I|#nYzLkFbJ`Z!x_P zo!0wV>wmuBD-({pihc?^u1XJf&9PN5+{GEQu0RYBn{RrGJsW+XkwR@^zzkR_5>&15 zuQ=lUFXc-l_hM92W|4c~@M>6AU;et_dG8jaSDzL(FiU~f%0owW=H{RMgx3&YN `bd>x(#<%`L){^8x|Ygi}| k*-P{x*TH z5ncFg+OpfXE8SbC0-=X+!9zs D53)x)Z43{H7PLy=6)!OJv$`*gKU)hdfoi zXtPOmlzfoMFdbFfA=bp%fOYS52_U{AUN}EH{#iyf_j}p&47ZB?yTfY#%{5TDLNws* zjEWILxF@ixSRyv8w3`co{iSC#vZrb)WCCKc)mF$km0 u9ucmQT`7I8Ei2=l?E>uJx*D@9oI3x}H}t|_(8EnKVg7E?oH_0gG@kxc zexAdSVX_EYAvO^qR%PGo5?BHGJ_CjhD(7-V$58qL#>9)G!k=Jt=6wD%6CLxutNJhy z`oZq3($Z}+KSgI3<>x|Y6{c3}+M=ZLhe?mHI`u*5?DH3#Y2V1M>kopBe3Uf4Gpy8` z*NdM`OmSMGfmBRf`rcIagso54-}|OK{8u335y5m`W;&RBu~=O*Of^I$XMLO7af4hV zAn0I~&fLDuzGXmByA}%;v0*!B_Tgqb@$YV}mo@KmiOd&Y0cHyWYIMAQ6T7pM8}9_k z8FW7lGxYjOINMjCsWw{^M5!ATrd2=e+vkm8>gsKRD+@I+Su(%`?$w?$Rm#(a&AIPJ zU%Qy9Zc>y^g7P_MJ58o-#V}K17qSh8AoDs9em6=HvW3AveJ$u2meKMk=4Yz~^FGn& zuR$v$&ghxE*JnhXrcF*Kh$atDYlr>{G@5qVI=z| tcFwAw(Ia$W=rSlrI~D6=Llq#C|bkD74=Iz`HP;|3<(!p$O7 ze*uuXJp#~S!80S3^Lp^=E4_$*GMeFXAas#7&?5paj4S6=21Q*S9v>m5(qPAs-D*xa zRMeP6G4JZM){S7veTh<72jJ?sdh<)=ye|~B@19o`%Eta{Cn?l&84i5G126t})i*9T zz)-HJB0J$l)n@R6Lb %hNNTQ2ZiEPqw;AArKm;~(-h)<+vJ;0Qos30|F_ur zKbB9nS4s{!cPic!XQMD;_|;MtG|wTwOnDlGDPNATR!OI67vU*1>(wxWz<(>=h*pp3 za8iWm88ip#1XZ Ji3Gx4 z3&z!)VCNRh0zx{Ki***4Emgr`+#i9J>|IJUHA=m@7PzTe=u}8nW2r|im?F&*0|-AV z%o?t%rh+&M024J^*ep`9Ncy!rR$px!`IS6DupJP2a7zHFJZqii Q@Cw=~!pnk|OsD5yWdHt*x>2tRL zyN?lP75yq$!vY8mUaEo<7ksUDtFPpdeYI^R94gPfk#s=x{+zR6P4%qJd~IU7KcX#w z(kGZj^Egjo`qne7(X65%>v^PdeYSqz8XAoIy#xY 6`o=QEv>%WJ 0OBH}a1bdwUIMn0tl1MogV06v7TH63LE1oR%7uv#>S71dm1WR9>VV-_C6<0W~y zqb5D3bHH=XeURUXyo>b>=YS^sS@KHdT_A L7@G!lL$GV-?%W-syq>*rZt_2RlQS1CoO^A zFBH-fjLE!NJh#NGc=}Xu0~`w%f6s0~WYAQW55)TJ=f#33F$#}zip&;Yfxx?K?p2Q) zhpVN67(N8x>v-RI>{*`>M55&gHilW+YPEb$(?u;ApgPl+9?rmjT6m0~59GW|fy!8o z;CXjmTlGd@Ak BNQRqcou| zat>DPY1NGia3AU>*21`+5I0yJu3<(dzSpE?+{Q(KtSL`ddv47}w~HVFfSZYBjU9ZS zA_gEpqh}cy#*!o9Pr6$Gq2{95B_?h@8rit9AFTjI(*mA8Zi?b5*-y5UXR1EPFZ?$A zT6@TCHQ3^DP*-K!DD}~m%cF3^XSRrKb)!P0|Arm}t_l-A1Zj{L&zVbe@NV8J=o;jD zmI>Nrek8x?Ll%H{GHxMcS-MDnA{)1-^cO`Vh&eX>#XJ#ndU|8b_DAB6vXf^1kp|F3 zERY9)cOLTl8F?L~f_UfWAmn4c8NLM|K~ymcQq4a=B77qe#QUo*FmKn}O~2KIcm(Qx z^+PxE`#`^}J}sydCHUU3J@?UBmQW57QF}4-#8r*9Zd6>wsGD!b$CRnV5WDvG#1Uit zLT%$Fa=f!F=Ut&=SL>OJg$=*>A!IBn+a9MPQcl^VCRd(`Sj7ld`+&2e7DBnrSNeO& zU5o$D&L6$y6@f(0QcCMzBc3y-!lkI6Xnsg2Em#P0LztoDYf0n1uR>>(9R$Id^k5Bv z 2gLLTdG`b|6v?DO64? ^p2@Vhi4hrkf7pN zR`c>@w&4v)if7|G?~ wtXRqyc1RPTqPT0z^0ipG%rz&o z`mDeXq9%y$D_Rgd?x}l`Aj6O8_{Hl2QqeU8>ahqV9j+=me6fKn=hZ1B$c82FQpCOG zlG|0!nz$MybHUm *`<0j^80j!sI~mdk9BsqQRHvpR@&*4>0ZlQNNmCn7CpJ3c_} zs8{y1s?+gDY`IWSzMi#Ar`Twl!#PK#BZiJqw>omFUSIQwJdrFOGW7CN71IXc@ z50M@Oo@gbQx`>w80_Omwur`9j#6V(uA~QdiOnf-}qWIl}2c9mg?p8(j* uhS^@G7Obu;raxrCWwu%U zg;=iTTjJtmF)+6@_!6?OSl)~GgdEV<_xH&eQw_?tR2oBFQE9sgWx@uo!Fn2FhG!|e zLhqQNuH1#)GB5b*nS7~xUP8w?ut)3c)_>aD883SC)mZ%+RIS-B5@Q04q!h`rx^>r& z$Mya@uT6sPY@hNoB+O<`r%#^?B?3<5n-;PM`cd7KZ!LE-9Z$CkRcC7`zNvH}!9wZB zH>2%X?hif=lnFJOfdk0TA5cA*z9UZc%&ZdI`&IlT)B$wjRo?2@0O?XvqJg#Ch05%1 z5P!%XJQ=}FqUW_;G$k+Jk8)AC0#nGA?LaqS`u=iGC|`tNO*WeL?Yb^+HEHN}!th!= z9@$sIuREjck6zsSX7r2)e^>ErvmHBG_F8tgTZ@3pOT2eM aIuUitmKpvE8YW0^0R8dK-nY>q7~V7S8^HG1 zIVyN`XgBqN`RE#tLf(fJHhZ4wqI2`HpF!F)G#F5X%dhs$DdG`s@k@Et1 hP2f_44%G(0x)42GX(K8mdf zM@yoIL=L94^bBuC1eg2eR(E`iGx6|m#2bp!EMnEM5fR&2+@GFD9^!R7oUeQ3yUw0l zN3TM(PK{AYf(Es9 KUw2pC3evAWGiFcdEK@sS!{v7F@MVV^5hmT#b(?}~iC2!=ieW5n zaV1zx>P$*fZj`?jVpBSU_^u=u%4vS#GmwrS*o>Kk>&Y`5b@7%F68Q*f%sWR?-T(4z zYet#80Ddx_f7!xI?wM&|t|VVS-;sjbmpqlQhn~1A5YrdisHG1Nb{e4SCU(W~Mm?BM zMM;5Whqb0HCh@m)607vU{-1w_CQxoJN4pYvW;EI3$zn+@*yQEG+fGaQ9&DmLZsJ1N z;NSIKF+Lp**G7WM$e#Ya(x=Q;EWw}PYc{zMPM47kU#1kXwp QK=yH0g4C_A;GAqKmr TfppDi)KL(G<)-x8+SsGE%?
k^aFZWR(2qA_4+f*g* zjDjE+X!FMPe@`rs_v*{#Nui>5+2`*b*m^VZ?Crgd1a6l|=^H^O>g~DL9`F9SdqIGw zYW(e5*OQmde}2<%Yt+vsXvw0?Ps?VX{N?V>&7VDAch&tbyB<8YJ9q8z_6Jazi_74W zuNF?f7+A@(FS}yDR#K};E!XJ0i=GXm@#SNN6k>U$x-aSi?CF2(ADLe^rbA-4IG~4x zP9YL?bGVEjI%Iks9$9{{L~R!@ivfS}!lRY9DEg<^Hy#}<(Hbu?XE&Za5{A#G-}Nld z<;ex9hw^mS6?ndpl|NGFDaqv}*O}HA<#M3A?TXM+yFIIehpE0Z4pLpKL^mns&IR5N zHX<=I$fP_yGv@Tep`?l}a^!G|03}Hn97>vNY_=um3F?Y7oD@|tVMFSHcm x3K}^d{ zU4^eMjp {-8o#WpyD+#`-e;qyfoFxoynb)G(@RxAia!zoo8H@@4WZ`?ePHH zRhKv8?O*dZB)C>BmI+MP!yb-QQ2QyQ5KN~bGd8b#7(|OIztG|1s@pg7Ezdlv(LeUm zbm%-&4e8tBIE>m6JwmqDCP=eR$vAK^Hj=lt0wjs zE{e(K*Xu{u&hL!m6&ykD9a8@i9vzeATXO}0B8&Gnas;BgN%hByC~f@xq^Lw~+`A^3 zut-kVJJqZSMcKc<#mRP3U}?KXD;;JGknY#8laibH>H+7k!21RF5zHGD{TpswYTvq3 zhxQKb&Se?TS;rUqv2XI^vr~g0Ld$q%c301;z7v}h(Y{!68UmxWLpj=Z`k;7sSIUH8 z`y>h1kwr)+Bpyud_@& d;>ELWj~%U#NbZVa54I+&z!G5a+mPKq#LEIl*E(re z9`$|y{>5_5R^x( {1#r=9cbLg%&5=s1(=bcg!h`|jUHha7tkC?kC +J!Q2(n@8WPIT1`1fAbcw^( zQ-PnuZsN&*TPl9xaIb1PuzDsuI;f0N`-(BrItXsLo #W3!~90~PO9oJ!o9Lg_0>3y-M+-nr&16IQZB}^xx4fDg66%@2+))OYL9ypD_ zW!H1z45h9^)otK|38o=( UqCDEk3MeNqJQr- zMof@mPgp$0Pe=CcDL*l~r{+|3L3?-Iupsr>WhzKqz@oSlbFrI$;r)%|O`_NuJDJdQ zbAz(g>ToOpAS#TNRt@sRE4~qX0-b8v0_xa?WMj%`Xa@gly7{yo7i{)HiW(#!bR5RK zI5C O*|v0!PgNC{}_PK}P(nNnIg6_*uwa!*6b=nrf9Ns%;x(Vf}z zwv{jn!v;@|1V}~SVwLoI=untn!xcF4ltCJ@8yz5GaKUs5)nLy~#iCTsxx=ED)Nd4h zw;@%`ztfT0G&x(B=ZtV!%zK;lrOj^DrTEEEKO(diC3aKA6$g?B EsC8S$ zffY4Ad$q^gWLK zJe0mx{=JlNytIr$A6)RD6;87-NQ{+UiAkeaRwYKEDdWj^zerNS*TtNBo*?(+q=SOA zNpyj*xv*#WN*KwK0&GwMes4n%xLm8-#r9Q-8b(y`2B~NP`DXn5eJO!b3iA2d5Y@x) zQ81*)Vs)))hhH*nw2-w{z-lpRl$QrCbHNRlGl*(XhBd`XULmN9LD3C%)_urf^eDF> zn}p`WqrG`Wl@UFA+FA6U;+(uKD{=lQBY${Twh2t=uj`rwMeJ#~-z*y+LPoO#u-Z6I zlcYIMo#s`YeV&&Nst#DbJC>B7*Lw)fj^-e5RFkl1k t`H==n`TX8&YhD~3^+X8Dmes$%yJiK$!auxS#+e-IMGM+<9rt)kG`=@LsK z6LWY3*D8&_h!w&el6=~a%baxQ w&n7lBP*fJYkv>7@lo5S(ir!hE zj(=?YI&)&eMZ1x~^c|zUwevw9TQIe7J1_vRLvCI-tCICPQQ|^t?a{rNomM8g%Q8QI zs)bz8STq)%>{2gss#&VM@S|sER6rZ<6;u#<=V1SD^K=yKWZ<7rF8S1+Yj&&Z zXk^crK##Q42Raz+KSYXT2YR8MECF _x7i#$7)5Wns_S z;wA@6k0bV*Edz`AvMEzgW sv!!>M<@_lCpN=Oj8nt7{gfS>w*aTE4TaaL1e7 b@$_DTptobCr6(?$f} z`(^H#HKF!t$GFhbY#VXc{@D d3E)!`ej1Ueu{J+9FE!%*VA-3SiUImYP6rVTX0mgks{KF@Jm z(suQ%O~o-sX#5MvJLI|z33{vJs8F1DhhfW^bocPUg+EGPJcc^mEt4p^`3iD{5@@7E zq*(g}Z21u}|IXEOU&g)p8alcdo&>~ikAxhdT3^=(2r`&Au>6|2wQHyUf8CG!0=hRF zo 0j-%7UQU-oF$UU4odv= )IN-|{~*M(YuDT4=wF(viVVV4nHcykMV} z{2UawJLDLurM65Z40{N1DT4{h9b8LX?;}7lB0-P-^}?u%ve#My{HYvJL+sO>Qc+FT zIwnOqLg1}dF8`3V+Bo*wfO6#uozQx&vX;zZ0tpnA>3(Qp#zbL@LxO=hkhEV?VJ$!B z6!5@~kdFbAW38cWSTN52=G_Npuz0zRN YGr5iMcWL|{dTod5Vi>oauAR4SY+cGm z+Jg?VJ1BNbAEv{!9EAK<2_+&q>72 >SR{R}R_6QAe5gA%(j(8a oz#RMl2W%(qT_&SF_C$B7 {#XIPqu2@(c;9Q)n<$ZBH1b35ET zOz?_-2X#L)$InVYcq1(zG!y&={OQ^Dv6aU;C-H;Z2`fv3-wr;cUH02r)rohQf5W8w z9zp-8_IH?~R@&FsRsoVi-|{D71(2g9fZ-6DR^3mTWJRx`N=$8lfr)gUV#XW+gGdJ< zE9_3@P7*L2%*zJ1YHIG>eI=$fQyf$*_8-d60GXjC4op7ihdzP4WKLN4qgS=ZPm?^R zKag&E%zz)+V%*g>cy=PDPvTY2VRypbPEu?z2}1{M7mzk*|C2M>izIIM|0Ykrc8XfD z*n`=i@oSs@$tJCXO4L%fCCHbLH%xiB1B(~36Sw>S2I#0yLMM5h$nD4oe(JF3^Wr am8zR*s6r$T-YS n>E+c^ZCppVNSG+lG1I)<(SFc@B;5TW}ZW%Dn824(|)J0R~x3yw>r-; zBoS)l!n}mP=oCV*E&9Zh3woZz= NcnDO1I1RGyP zb78T@9SFs8cEKZ~hqr|9`)y3+tv`u1?BnB_5R^L0$=wi6+X+zk-4u%3Jzd9gS%#qV z-0_DSQr2)5WP)=Jpm31&VOo)SYXskkR%Jte2*`M+cit)1ZFLr-waGQFAj1a%DtJa( zi0Y(u`sWlJfw?on#9V?5PTr|KYc&Pf!X|+;{a20i^Gd}oNkTDKqJrf~vEL;Gd-qr4 zwz= DpW29 z9*0Ta_b!2@KABLYP!SB)aZaKh92+T?0Ev*fsZh65V%j`4(mb+wXe6TBGgq0jvs4{} zNkPJcK*%f+L)|PTpqOUF$v)|nr4*tjhB%C%e_X*fOL3tkCZ{P&Rj5^zWDScV54@su z7jBiA+PEIX@>ng_qHJRJj!?|-5r)D1bN5B{Jq=WqRU~~c(kVsrX}SGwWx?i|Q`VZa zEOTIJ{TWY6iE%dj6R9{^3oEBJ8M$U$SnOjDbBmN %*sUT~QP}|Sx zDffJ7y4OSvz!%`Wf7@n6OoeESV& zgfHlc&u1fgZP!~{iL4$*i-e;MCJF~TX6PUJp`CKkK-LTf%Vgf(I~+Ew=1R<+ViwzH z|6XgmaX1zT%!Re8b+>$ltKGvsy4@^y!1|&|a4(jG#RFZv{e&_70IeqAX#K1^JUMaG zL}DQ?L~W5@t$*Qg{hHo5Gu5*d(TTW-z Ou>VA z`Q(QUC5-j&?Gn*PbKA>|1sT~Hz(t_*re4!xAHYb3H8Xc*K11UDL!?ezV%_RjtGg z?$aDM_CN9zDuY2uo^ ?_A6hli)}a@>py_Bj&~yc+4s%xexO8!7a_20kaq;rLQl7kvn$mU(dD+sHV5$-M zF~`73ss}A2?z%j1s({BlA_2wm8JEw6y9CZ{FI5z1F=!pAdxf~pl-_R&_0CC9%|~#e zxj`mHD(eO|& j8QZfcz-wgsS7yz4P~9P3lC)z62W9F41xYw{~2vg_~j zjDzvf)hA}u%z!Nj2Ora$+^WnestP-Ff}9kA? oH-dk!JWdW#@384y ;0(`9wxpu%4uCu#|{g4ckb(NufU*LcH)+ zF`D$5{f%pa=@&%vRdW*NQb@zti@L^PAzemh6Q2aAZ5tout(Dtk=#5_k$v%fgTVl8b zr)LI%co(b>d^+oCh=DV+a(q@ZmqmYh)p=gC(ZeUxCleg0c^L;pc!EcaGR9XeEFH69 zU05F2sINaIPd5OLbKKy` zpAhJ4Bs*Kdfgy5rY8Ja$-V!gv-Jm2N@Of$4HoA5;C;Y|!S`F6OTL6yVnGpHRj#p(y zULpPGVfZEgIG_Lm-1LC9ux@ RmJUN|eCsaYLN-&5EV;QlT!Geo6YfK3^aj|Z|pNs)9F=xWy(Q%qS5^9Z$IAW$;y uX*so*p?5&LOMG=*i2vAW!>^8;oHyLor=Rt|asVD=^QLVZZ~cvQ^1lJJHe8|r literal 0 HcmV?d00001 diff --git a/scripts/generate-icons.mjs b/scripts/generate-icons.mjs new file mode 100644 index 0000000..0c91091 --- /dev/null +++ b/scripts/generate-icons.mjs @@ -0,0 +1,59 @@ +// Generates PWA icons and favicons from public/jei-logo.svg +// Requires: sharp (devDependency) + +import fs from 'node:fs/promises' +import path from 'node:path' +import sharp from 'sharp' + +const root = path.resolve(process.cwd()) +const candidatePng = path.join(root, 'public', 'brand.png') +const srcSvg = path.join(root, 'public', 'jei-logo.svg') +let srcImage = srcSvg +try { + const stat = await fs.stat(candidatePng) + if (stat.isFile()) srcImage = candidatePng +} catch {} +const outDir = path.join(root, 'public') +const iconsDir = path.join(outDir, 'icons') + +async function ensureDir(dir) { + await fs.mkdir(dir, { recursive: true }) +} + +async function makePng(size, outPath, padding = 0) { + // Create a square canvas and fit the SVG inside with optional padding + const inner = Math.round(size * (1 - padding)) + const background = { r: 0, g: 0, b: 0, alpha: 0 } // transparent + const buf = await sharp(srcImage) + .resize(inner, inner, { fit: 'contain', background }) + .png() + .toBuffer() + + // Composite on a square canvas to ensure exact size + await sharp({ create: { width: size, height: size, channels: 4, background } }) + .composite([{ input: buf, gravity: 'center' }]) + .png() + .toFile(outPath) +} + +async function run() { + await ensureDir(iconsDir) + + // PWA icons + await makePng(192, path.join(iconsDir, 'icon-192x192.png')) + await makePng(512, path.join(iconsDir, 'icon-512x512.png')) + // Maskable: add ~12% padding + await makePng(512, path.join(iconsDir, 'maskable-512x512.png'), 0.12) + + // Favicons and apple-touch icon + await makePng(32, path.join(outDir, 'favicon-32x32.png')) + await makePng(16, path.join(outDir, 'favicon-16x16.png')) + await makePng(180, path.join(outDir, 'apple-touch-icon-180x180.png')) + + console.log('Icons generated in public/ and public/icons/') +} + +run().catch((err) => { + console.error(err) + process.exit(1) +}) diff --git a/src/main.tsx b/src/main.tsx index f8b6a38..a35e6b7 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,5 +1,9 @@ import { createRoot } from "react-dom/client"; import App from "./App.tsx"; import "./globals.css"; +import { registerSW } from "virtual:pwa-register"; createRoot(document.getElementById("root")!).render( ); + +// Register service worker for PWA (auto-updates when new version available) +registerSW({ immediate: true }); diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index e2bb304..9675507 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,4 +1,5 @@ /// +/// interface ImportMetaEnv { readonly MODE: string diff --git a/vite.config.ts b/vite.config.ts index 923dca2..aab3e98 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from "vite"; +import { VitePWA } from "vite-plugin-pwa"; import dyadComponentTagger from "@dyad-sh/react-vite-component-tagger"; import react from "@vitejs/plugin-react-swc"; import path from "path"; @@ -8,7 +9,39 @@ export default defineConfig(() => ({ host: "::", port: 8080, }, - plugins: [dyadComponentTagger(), react()], + plugins: [ + dyadComponentTagger(), + react(), + VitePWA({ + registerType: "autoUpdate", + includeAssets: [ + "favicon.ico", + "favicon-16x16.png", + "favicon-32x32.png", + "apple-touch-icon-180x180.png", + "robots.txt" + ], + manifest: { + name: "JEI Stock App", + short_name: "JEI Stock", + description: "Stock management PWA for JEISA", + theme_color: "#0f172a", + background_color: "#ffffff", + display: "standalone", + scope: "/", + start_url: "/", + icons: [ + { src: "/icons/icon-192x192.png", sizes: "192x192", type: "image/png", purpose: "any" }, + { src: "/icons/icon-512x512.png", sizes: "512x512", type: "image/png", purpose: "any" }, + { src: "/icons/maskable-512x512.png", sizes: "512x512", type: "image/png", purpose: "maskable" } + ], + }, + workbox: { + navigateFallback: "/index.html", + cleanupOutdatedCaches: true, + }, + }), + ], resolve: { alias: { "@": path.resolve(__dirname, "./src"),