From 3301c5967e8927f7991b03407bff795b710436f4 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Fri, 6 Mar 2026 16:14:26 -0300 Subject: [PATCH] feat: enhance SEO and public files for Velxio - Added comprehensive SEO meta tags to `frontend/index.html` including Open Graph and Twitter Card data. - Updated `frontend/public` with new favicon assets and a PWA manifest. - Created a favicon generation script to automate favicon creation from SVG. - Implemented `robots.txt` to allow all crawlers and point to the sitemap. - Added `sitemap.xml` with public routes and priorities for better indexing. --- CLAUDE.md | 8 + frontend/index.html | 201 ++++++++++++++++++++++++- frontend/package.json | 1 + frontend/public/android-chrome-192.png | Bin 0 -> 4798 bytes frontend/public/android-chrome-512.png | Bin 0 -> 14242 bytes frontend/public/apple-touch-icon.png | Bin 0 -> 4451 bytes frontend/public/favicon-16x16.png | Bin 0 -> 532 bytes frontend/public/favicon-32x32.png | Bin 0 -> 751 bytes frontend/public/favicon-48x48.png | Bin 0 -> 1126 bytes frontend/public/favicon.ico | Bin 0 -> 15086 bytes frontend/public/favicon.svg | 17 +++ frontend/public/manifest.webmanifest | 51 +++++++ frontend/public/og-image.svg | 142 +++++++++++++++++ frontend/public/robots.txt | 5 + frontend/public/sitemap.xml | 40 +++++ scripts/generate-favicons.mjs | 77 ++++++++++ 16 files changed, 540 insertions(+), 2 deletions(-) create mode 100644 frontend/public/android-chrome-192.png create mode 100644 frontend/public/android-chrome-512.png create mode 100644 frontend/public/apple-touch-icon.png create mode 100644 frontend/public/favicon-16x16.png create mode 100644 frontend/public/favicon-32x32.png create mode 100644 frontend/public/favicon-48x48.png create mode 100644 frontend/public/favicon.ico create mode 100644 frontend/public/favicon.svg create mode 100644 frontend/public/manifest.webmanifest create mode 100644 frontend/public/og-image.svg create mode 100644 frontend/public/robots.txt create mode 100644 frontend/public/sitemap.xml create mode 100644 scripts/generate-favicons.mjs diff --git a/CLAUDE.md b/CLAUDE.md index 2063761..19f51c0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -243,6 +243,14 @@ Wire positions auto-update when components move via `updateWirePositions()`. - [frontend/src/pages/UserProfilePage.tsx](frontend/src/pages/UserProfilePage.tsx) - Profile with project grid - [frontend/src/pages/ProjectPage.tsx](frontend/src/pages/ProjectPage.tsx) - Loads project into editor +### Frontend - SEO & Public Files +- `frontend/index.html` — Full SEO meta tags, OG, Twitter Card, JSON-LD. **Domain is `https://velxio.dev`** — update if domain changes. +- `frontend/public/favicon.svg` — SVG chip favicon (scales to all sizes) +- `frontend/public/og-image.svg` — 1200×630 social preview image (OG/Twitter). Export as PNG for max compatibility. +- `frontend/public/robots.txt` — Allow all crawlers, points to sitemap +- `frontend/public/sitemap.xml` — All public routes with priorities +- `frontend/public/manifest.webmanifest` — PWA manifest, theme color `#007acc` + ### Docker & CI - [Dockerfile.standalone](Dockerfile.standalone) - Multi-stage Docker build - [.github/workflows/docker-publish.yml](.github/workflows/docker-publish.yml) - Publishes to GHCR + Docker Hub on push to master diff --git a/frontend/index.html b/frontend/index.html index 072a57e..1178b48 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,206 @@ - - frontend + + + Velxio — Free Local Arduino Emulator | AVR8 · RP2040 · 48+ Components + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/frontend/package.json b/frontend/package.json index 29817ab..cbf2afe 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "generate:metadata": "cd .. && npx tsx scripts/generate-component-metadata.ts", + "generate:favicons": "node ../scripts/generate-favicons.mjs", "dev": "npm run generate:metadata && vite", "build": "npm run generate:metadata && tsc -b && vite build", "build:docker": "vite build", diff --git a/frontend/public/android-chrome-192.png b/frontend/public/android-chrome-192.png new file mode 100644 index 0000000000000000000000000000000000000000..6298f3d555cb760ab78140c84ebc4084826f1e26 GIT binary patch literal 4798 zcmeI0`9GB3`^WDwOqLn@I+l>PgfcRigt10%Td9yTwro*D?~<%Dwjq0#Y}pHmL5Z@= zh)~qXR+bo}vXp%{gYTrz=l%T`zCV0_xX=A~oO7LXu5-?PU)S?>V$WHcz&PQY006+w zni^R%-{jv9JA_$UkNF$|0RE`6M*0^6a_1kJ#Y@-@c2XYY5ju%$Z)OZRt>sZ%wXr!C zy0a0~SaxZ?*|@{v%B*=l635tw&l-Jst8rS-(nm&lSUgGJNh6f?nCWC;ysC5xY|7`2 zY~pEaWqgd$)aQ7Qv3`bC(rK|x^A8ulJ`JMhR;{L9-$~PKsbAc8+#I`5|5TErz`in( z?~@@H7t#`SqpmF4>Q-WhedUm-*mb{;gQ|IVSU!G2>cYWbq_NRqBs@WDJu^=f$2Q^9 z=N~LHWrVybt8&zbTQ)mf|B0f|>n~h}MrweOW7JH(K1zQ8ezeUlI!CtXZ06*h%tAa0 z)d~Z?Cdt&D`f|Naz6)WE%fi161C{qVZxvXAINSkt5@d2wb5i^sPE6hqUg!(f+q%TQ zuP)GXUT!mfi6x%=+|XI@I_^VG1%26%okpbfBRJXQJ*iW@>pv4*x0E9dptv~Q>FsG5 z2HX;ML;>*z=^pVY6FQfcItIaQ-PL21+WQY`%CZO`ox(kw!?{rF@()4DD@~Be!+M^g zI-oPC`)M<5k(dB6z;;)$YDamG<7B)PSRg+}PkCx z%tmf(h*&(P`O6Bt#vxKJ!GPse$#K%;oa0-s2{p%2i|1C_NeWqa9wzqpu3a`-=^fuI zYE|wf2U1rN`j7bS1_9F$JBE7XW2c3%&AvySqy&Yml=`qUZDBeIrcxuLhgmY6;N`v3 zd{r@RHL|?>pqPE8v3?`&MLnA79v7nZd}OyTDg!@2Fw3*O^4HlAEl-i;jST_|gnDtt zxx@gLJ5Fe;iMPmkIvs0&nm2=Uo~3VmFPY3*s>)@4PK3KNgi3QX9Gu3on-Tm$Uf3|t zb3(Ms1(s&Ms0Hw=eL3s==6_pkve_gOlUfJEptk!J>}IhC_R!ZJAi|XE``~qt7hI-6 zbk|-TE|kO#-*qBwZXX-@*!aL#2!b3UBts~MDWV0+7`a9K#SVJR)$wfpN)S&t%ff9U zE=oNv>XiF;{K25wV}j5nxo~5!H2v5*tv=$Q(p)RfnUG1`6KL4UFM9B2fQN69T{4Ox z>!trOsTRFy8zJGMK_1L#F1p(Mdz<7DdXkWUFPHcBIY!t@ns;j^?*QUX5|99{>BlAq z&S@aXsob*^nG*CNKTP1CRYV|X5aLt?X!YUgxk(R~#n6p?=0~TITUe!Do05G+O8?rH z_4bU;t<7<{^RN7~AQSs$6WSM`V2OVe@Rs_Dqi2tOpbU8K{o@$5MCui8;*-*7r_#fo z6q5Ec6c1ZYIliEsc&e`Fy;?f^QGjx^g{3_POuFv&tr5u6KFs zpSrIx&|^@m{2Su>0;c%6OpuhP%n-;rceF9l4x=K7QF(^miTTj}JuZ|l>J*3K;35ZnxQVK*w)QA}H4$y@Ys zsUZ#K8${vg-cIks2;|~9SdWYVcU#sZvqxWYr4*)5c29eLOF|pV3DiKSTIf>U=unNk zy(eGjtS+$Xk)Q>b!_tS7udKA@=?U!mIq#WaqM*Z1i*@XG|t$O{WfWtWkc#}#!K zXiHHQ2K@CFF-Y<++m?y(iBuuYyA``?dJ(pJk!r=B3paON=+h7F+r)h{VB?xE7Ve^r z8{{`=1S5eeOuD8;17v|H!CBnej}Be+gI-GpgnF09t12hA52N zZMJ(bkBzLYBdzicb<@{=WZ@RMOdC(he+0aw=4PU?%~wVQL@Wsb-hT_r>Bb-V2&gq; zKD_usV_TegCBi3GGfSwk#saan>9xssUTT0#sKPfx-G-Ns)iv2bYBMVSx|zW1ffJwD z1@Ara5U>?K=Zpvh6z^zmG$%^ylVH(Ty}B58;E2dbXE)U8 zn*lu_3uFV1({A}r89gbiTPL(%K){DNcwy007+!`?9V3+qvY{>%J9?8mD;!_CuKHgK zO&O#%VrkFpy5Fgi8l!i@Q*u5t=|1e#s-C}8Qa&rtjMB9*4cWBULEL; z*~vdYeiGB`(_68u$f*WUbrx0;dRe03psnH9rZtzQ`z31-h5mF>j;wq5m#It0S1>Yt zGlxfas47jc^-;`f-u7&uh3bsJHT%1D3GqDQGFDgKmCfmokg9TJz_gLjxf%wI#3=AZb zIR9k$Xr9SHP!klumkf7wQ{by>PuKm-LVZ&D2auG7z>p-p`=B8QElb0G4?efzG>B>A z);z%@VVKn`@9Vk)^kE+`a-jgZjV{&sN%>`1l0O<`S(U}To$YWRRP;p8JVQcDP8VXj zs$8-5@U^?l2easq{K^a`oVK?p7VInaVcU&p!TTO)2~Rfd+S?cbZY;Y#5^jc$;QM8& zP`=*_*@xg|H+ukYfm+gK7`k!pp81?B z8?(SLERrZ9aO;Efc?J=^cbdLm!ykuE-Ld2elNSoFKm2}3ziZE3UGLM{FG8(H{+>SN zEhX|KdFlH<$d%4~**?l6l@P*@m-7Z`@it(@4S}X*&Z1udVz~aiD-$Su+2)q$@Ay$H z)`O`y&&*c#VLwh8?YwH9W>v4(4p+$fG$B#asuX6B2IGJ@{pK0s?XGTMweBmD-o6?W zn3M#{&M*Ho|Hy>5GLfht=Xdv~h}Vyw1is3TJF6<^zqffA4JW(c{tGw53XegVNN^`t z<(;st8he52Ee{f-`6Ao>_aGajP(- zsRuZ7;ziO!F>+Vbze!^U=DQ*$tGp2}g`<%ZLWoC9lf;~0H!TH{ zCRD2&RIXTNUWtCrlOoYBCjFD!V*KiNW(-ToM^bE5qOk4Xhws5z)*+%z8VM@ve;J+e zarc*OwHxGp+y+e!M`95(PY&mO9fV9Ar@X%VH_hkxM}QHmKh$}AJMk!*f4XWP>~Y4^ zJ_r3(>PU-W9-CBg6MZx8OfD!hhq9Z^JWSrlg#1y-kg0+!X@v4#Rw++u=y*d!PNT87 z6$34MIR=Sa+zZa*K$70-Yvpb%jq256wzG=z&Ttb6ulcOZHGZ=sqrw=DOIaD>ID&4H9Nnyh74mrlAJv%>vUGiRDW0W$tv@?Ev)t{EM$=H6LxJH{I> z-W3?I_2bLD1JS)j(`gM-wM|5C7B%ExlnUPf&>Sa7x=aqoH6~4*zZi^+$v^Lk(=a6j z$XBc5%|}k5s~CO#d5;^Is%hP(Vx8AT-R_R5R9}UT(qt|jP}94^d1-UL1lnTdd#L{V zy!&PBWX{zi4-?}mSBZ&@WkY~uz-T+8I8LRu*7%l0KuWjD2t07pB4t#(4-A%O)4DI{ ziT*gy#^t8LvUtTgpg*HhWTo|<4))}=fG{C0$8MIp$CFtkUBMFJAZ}XITFJe3E1IFC zBolm@SnF(CY15hWVdx4M~R`) zOKv-(1FDwK-PeRCyfz$zJSPjf!fzq<}Kl8b&?#fVo zU+BXx8pGHAS2x%(_B@<$9m#u0-!r!I2iI`s_eXM0}(@ zT$uD7Gs*G*9q63i+m5?pgel$k+FXBR(G&U{1trm~@Kd?qpvQyss=fHO8fXv8U=-Wa zHdY?$3*>(U6pch&c`itBHAZkr;6NBdJNUuOh9Gbgu44Z{cJa4%wL{L`t` zp~Hd*4;S)7@$1VYqc3{G)mp)*`Jf-(jiZYs$Atfe%0D_VJ^w@jT}MYZ?K7q7SvLS8pBc%qLqclRrnu(^-ph&^p>6LbnKG_Wn3jO_o{(9dQpyNm z8uj#pyhXa(`JP#$>2*bh7VizOh>Z>U^PQsloB@av6>ptI?u~A|THcFV*@IYGr@RB8 z+D`Ha)&~v@b9XRe379zcBg$`|$N^ox#teSTC-n{~#}Bj8`QT@)a9bBH1wH6udXw79 z-2=HQ69|+>^Rcn#V{chVPrbrjz}^za;W;|0Pv{x?I5@QzJX&1ze0GbVc|1zA0Bl_Z z-a=h_j^|@(nVXv8wazR4efa<*J86{3{za?9A5TtrgWIw?^8HjVfI1%k2G$GGr6%0g z7SH5^Vi!2}d2c(G0<3C!ghaj=&qF;<?A_kl!LtpT19N!jXgga8eP8s|A z3Z*dyooYwLcmnOMeyAd_itnK3*eUqYBEKFe&t$)XSVLbJE~(rm?uqoyG0zsu6hBlT zw|ICA>Jt2DE{&^vQoxZnA?wubHrdXAAr6#D|CXn8U6yQ7pbG26hcFOlhbn8&xT6>I PUJ5vCY-vV+$x>l%SyNFxC8Q{irR+&| zGljH~B9vtYJ=wBlA2V}*H>&r2kK_CIJC5&9zXuq|r*{e;@iiE|f=cLxOQ&rX$JZIcqu@M(8 zIA?gGX6`cs6L#a$e#teWF7k0Bi92Sk>lOQ+xn=bKW6?%mK)8pZ9=TXqc72*2emt}% z7f9^9*I3GP*rF%j%Z%Ogpl#uCkcpQ)0L^w$FRaURtrD7;S$f-AHq$HNp#Qu^d;w*a^pc8DtgcbT76$b_~1z9ATIAKHUs&?+xMJ`+HKe=~)0$ z62CURiaRJav-5Sy;2M%!DnDFzOj1NtTJFak!<7K3>m>2( zp6;s1?-I@5AaZWnv=j%iR>;kk)KzdD^Gmb}8U#s)S6#!9}>JYKER_}^zK>J`1Jz`fIr#SbI{2^dcd#i7ru_{AQ42w((B;$o*S zILF=DjnN2}GuGkySR+NdTjwBfm>34TVnZIhhV>sjmy-wE#=Y(*3ci1mO#%g5bMJJF zjHg~=6gE+=i2xUudtFtIUZwz0Z*Nm6a69{?!n*F@sljI1@?p1f643M(g09(1yYB~0 z3{t@C*y!|yiYX}ouaU&{He)VI;mX-|9>ronZlUiOa#)U|#})#Vqv$&cge*VMWQRz| z%>_IX0F-n|+>Eh_Kgpsn{`5+4u8o{m!E?LFF(d&cl?eFqWDz96wo@w>25D*c?&VrV z2k=$}^8qEb2-q`UMsZh2Wd*tAP%Z|};G~R9XjK6aCW$*;yvFPbNZp9UW^kATTmBIP znnub>n*=*^)`sR)C%Ntc?%zpd@>~GQF=&>=l=tg1!mE=`?gX{dJmQ7-=$$*AIW7El z0rL(Ia%ah@6}(_o3@|&KL}=IE@j3YD0be6LAn@MLw14LZ?jQo@3PE(U54Ym?xVl%- z)24arWjPQqKE4I_bZX097n%afMqx7vG*N^giGCe^|7mSK35eoC4kLrq3H1p0`_Bgk zK}}i712nb2XNdvt`h5bgq`|fstvM{{e6tGk1P88hMR)+r9v4VR{vKjo_vWM^*wr7W zaKXhoV$g%9pCZ4{>x6ki*$+Fehrr1DJp#GS>tBpVMNQ8$o02#dNK8i0T#af3tR5%u zZ0eC@82PdEW=>~>akD;v1 zDCv~V2${>~tzw?7)qCNWkL(d41=};ttQBZ2m-jKkgrEhBoc$*um^8EZQ-)uCz)-w_ zg7nWtm>de4o&lo@p~E?tN6Suv^1>JWmC z1sktB51Pu63OP=_B~EqAbedCC5wpxP{kiQVFWb3`8Irjor*AGzP!e;KteCJ3xR2AG z)yL8-Hgt3EwNDt_#(On~R+I^Ei|vbBcSIoYs513V%pYrn!5Ox`s9XJ*khxA*{zq2s z#oE%j4S#u>Q*VOQlZa350{w$a7fDL5m^+~vJZ*)NoBK<}nfpA-;*zBT zZQC_hs5b<@TOqieZ=E1WV!L&bY?E82#&R-3&7-}pc8qg^RiCTIl9LW8E4>bZL-|n^ zTi7;7PF-zQ)`v`&^X@hd&=4~uVv*SGeGJD4c(*f*mjm0T>3_Or%tppgq(wlr59h;) z5r4H!d1lpQ+=ZM46%{;S>hj#THeeLw<%F1)0s}+Cj|u8Pb6QMT76hS^ z8I;7}XWUs74D88EWJd`uk?Yx8u{$9h4#j~kF0EyFdON?8))J+0N)f2vCOrP9ly!Rg z`^WC%%(UmUk{F8r@@o`qgY%D=j3-T>7vD-iuvrZGty_|?zKjJ^!VJD<3ZRKtW zQO`Y{JStG7XMr=Kg7Is8Vor9ON#yP-^9U(hAJEi=Z13XIAFQV0v9Ol{Eila%zN;!Ug&rkhy$9i%>@DK3QBF7u3v^$AJX!3s1rkOL6SYfE!0kAi{I(yWdo94fGz z>T1AyR&01lU^QA1@W}>@`VKJgEv15%P!Cx0^`!e7fx3q@^)*BZKq}A!M46XfVxBr^ zic?5(p#eVqc3dPHTZZps>4f>YHjtpPNFd9_V zAE&>6xU+JSr2S%PE#1tnO~^I)BS){_K|N5!+&E>nFW2}WDykS8(05g>J%rike0F0&@xTK?6CaTp88yf8bLrG2Rz zL8;??6dBZ0A$mzX#Nj~dvt{{QY2M0cdn(doSx5xWHiF6rtIh0}`KsFpI&&rvv+KeT zr5V|U#C`cm%j7M%SS(Vo{#68EOd{@>C3Yx@`Q7~9%D^4Z@@)X;EO4inHN8L=ebUB` zYfHW71Gz{MM7+jW+S-P;J#=yJO`zaEBblIyDbOwkl6Y=dCAsB;H$zwBUW0uhDjmZR zMHOh7iCgACHx(2Vkw72@7(ZVYR*bJHW(o_tE{?t3>O0-qB>!2`m0rE}U96+j1s7%$ z{l1p(KgC>xpQ>q>X&OG`M#?&8;_<`Dd0;Nnd4Or~o=Z249dHUw{~$s^{(wV(?5e7V znPf(#D?KC^&p|TZm*N8mb1v3{PLR#kZS$wd0=214yswCwLXea!e0UX^cOd)I;JKG> zt{29J^t_)>Y3qa!4tokW_m53MEr*j=%33MOhK%Jq&(+khWmsycC92p0#s!qv>@eQE zSlFH>1+Hc)vEUomT7Mx$f-Mu63A}v|48P&m=}0C3+#nDb$gv(cnNt?(eEgom~ZZa+c>IP zy}LZ<{SB80&8LgYad4#b8xkY6a7~jD4#r;&Lmt^H9uWk1FRn-23XEJhXd?RDK@uk_ zEHLo7R+Gwhnjx4r0PZk4&$);MznRkEKfN7HqZz>tJPqt1Vus>?nF|`soL!A;k%v+< zOlZiRo_DPyst{$5N1TF)3JnP}%#z1Y2!$OacYmo1K;4{$NiWo+<3J61PTkCDG2kbeBIJ(v?ptfKA83C68cU9})qntH72%X1wJ)^7 z^3nkuNNaSUt0eQfccU8&zQ*Y*E10>5Redgpfe)zMeIeMzEt(Lcbkz|;@Aj(0ycg0K zcyA{c>%PJjIf^mbKT& zm~c|{x5y^@iX+HTv55->gNYrMm)7Ed%g~RHDG&OPAu;wu%_gVR3lke3$l`k4{HNsE zG97_CuJ1w_9Pk>=#??3=q~ z@FBtN@369epS&cC>8(FaWL1G@L?DvL{*Yhud93sCO%0!N8aIVjleEVS>+_z03bFO; z6Gqak6JeM)qj#GMq4XqXtIs+Y#Yv64kBubolX~+=SrW$#sylgs31w%mRJT8OrPvFA zgEq;$^|{z-S05`Mu9JUMPnuVDc<18y!Uqzbmbx-00)(u2C5nN?<1>(jX5M>d_6m62 z+z+?+3AUotl??P3$fwza(?ApMjig`m8Mj`~*F-EY(|tswo^)X?29oB55Ds7)W0_JJ zp5;#ImAz>pJ~gCO67Kf{)5LY~c7gl7yU+`!J#pjsi332B@rF$v8^$MTRBOI^F?O|} z{uLfwL%N$MGse>yL3WrUl)p}GWYsp3Pp0s(a^dZ$Xmoi_`NQ<{KC@zT)RWTGDLO3+ zS-l=Zah>4bU0pf3<@S$3FV2+!1UTqEvV}Y4G3PbBLvZjr^-ADAawE+7>ln>U5a!RM zBX7N3hy8E==!u}ri-WC2nyU;}@COxYPQH5LviQPTdU`f}eryo25QNvSA6-bvxeJ|N zp+rWP%6vvv%*gM4d{pt=K1ODz4yh(ZL2y`X&)+M~DuWa8dO(xFKX>cm{0(rO1Su7D zLhv*Vowoo4z~WMGxhfPKKnKtOpYKf^xNbXo2TY_Xz@`%=&{;%ay zsSOwN%LT9wv>+95;vm1#@Vn{R@I`-MLVCNx4d1GGeoIbp|BhgfO}`|Pu_d_c&z`B- zQNbeAzsVPD)&DCc@&;{}OeAw5yINbUjl>iHi60<-k8gMToQI<5$6581I;DcqX=c<6 zdHq!Gj$dNd)ip2K^{|-NVS)K*la>hkQ;Zc}FSAHFsnoQe*DK~Jl9_Pk^prcgu zQnp=i0>AjPUN`!KoUTn%Dnau;yN*oUFSbg9CK{2qdL(nf0_U2;Vr^&F!M`ZJm1yJi zvEDx=t6zQ#ZNattY5JSooxNeaI~^pK8j?|rJQlU)rB6=n}0}@Hb;M;J^zSTtaw@+^fE}a_6R-3^K5;fTh

*+D8^LDO3@>rbu$ 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 B+86); literal 0 HcmV?d00001 diff --git a/frontend/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..96d47c657e8197b0c274c20c334c95ce7b20b5af GIT binary patch literal 4451 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 literal 0 HcmV?d00001 diff --git a/frontend/public/favicon-16x16.png b/frontend/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..59bdac41de2efa6267da43d9ea526a9f4d3604b6 GIT binary patch literal 532 zcmV+v0_**WP)ih?3|1Mz}Bg{X+&;02u* zP{GSWcj66P*f>HrI%E0Q^z^ij7hL!)y6T*&`um(ybp(iMn&rtP>H%4ZXCYPt0gPCd zH6z@^a+=-@iO+@HBcScBK#^^G1OlQ$ghJ-Jl=3I-42gmgetBRg%JIJKBVxiZ@+4<2 zdHJyawIcEE8?&M9p%SI79dAKj=mpMC=y16sU{{SoU+M>}L&m28$ppP5J(uD4a3U?x zC+q2SNeQHK%zX{}Zwrjk2(CZ<#^u-lkZ(*Vgl*x~_8fBd*ATB=hQ2%iTshG8VrhXd zMU0OEk!BbZC2)${5uEShQ6HzY6T!t^#A~9Aaxg8h>I!f@g;F=hBCsompiec@<00p_U4wmsNPMGP#{&JB!2?R*9z#&#aI*v>bP W&(&phE_O-)0000CMVAt4?gfJaLKL5KnxAQGekg@Pgo zDnt<)E;OM?fF=@=C=5peF}9cg+}h{M-d)y@L{5>PVZ|8rr5AO={kpa~zU4l6# zrL^$9V+bK^e9gsTbhij#yLbXpsZ2o#x`V(ORCf&sDN0FK9H;o0fGdE56Uj$ppfs9C zNz}XW1Z5CY`&Ps`79o&zPWxBc#GC&azm1Zp|HW05K}_v_I1?C-BO;y7+(7W_@EI`$ zFtzIlA#SpOj}Ty?Zp0M8)UE>own(LhBvy9sfX?7M4P|iZH<4{S^9hG0>ytU)4=R` zc!ERs=Z5?|g^&(dozV}X7&JOMoB zmFx&CMLDY$M*YD?I#)>4IuE0q%~AiGTa;LuN39_sv1+4K#8*Fgb3zl4gQeh(n7RO7 zViH*xn4Ll`nvIEv?(FY-bqzJYfJKywIP8D`o%(}*0gP#l>%Y_c(b*TUFJSKoP|I)x zmO|eL^p2z0n0SC?xE8VuZIp_(_JPJmv~1XjtcZ=M!+l`uRy+ttIBF|K@S4a#KN`&o zXa@*w`_E{-@QkXdgVgu#KHjMD=h6zL(Mg;s@))+2$Spil0MCi89!NNSB}_aX^UCls zI9ey>tRZ`RBnuDDKtKlTL#ionz_b)i?LovMpNhMCEXTcsFDBn0VhFx%F$FNS`$6!Z zxR@Lku)A_Ki^s(o+)?KQY!?=R%jtA8ndpStn-@4Zs^ke^Fd94@mvMU?Lq>9N?I4(N h!o8kP7XchD{{c<#q8(Obcs~FD002ovPDHLkV1oV#OtSz0 literal 0 HcmV?d00001 diff --git a/frontend/public/favicon-48x48.png b/frontend/public/favicon-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..ffb1f6f91e1759f85cb00086438c3ffcc4ffb0f6 GIT binary patch literal 1126 zcmV-s1eyDZP)C8!ddrm}sJfr#3zk(MF@jpwgRYZ-f|4v=Is)jXVkq zO-%4rvs5%-C?wHH;v$>^$KCq>_E_B8+u57BS0v$A?9FUuZ@&M0-#0rqbAlu!5(zV# zrFO?5QwTC0$4TIJ%@9J^=-WcyRxC!nwryLuT|j{V7~Y!6(0w8&iRyoZ7%&XF2_X~Y zBToQ~Ph!-cIyD|`RoE-=#zlx2K-Fbg02`mlIQ=MUlQvJ{N))&Ha4n8*`yhrP4no8b z0apNwZ^g(Dmpbw@*Wckvw7&NoiC^DPibl76kR?y-gop`OKvPo!BVT&Vdj_rR?WeAGP(xowwGZTVy9?sPIMu3I#4eeU4wMn%Rne~3xv-Q z!sh^$fv!Qi?d6E~?17)~KmxU*WH)Z7gXW5<%l7fjj3^}|M*BbvEcz5 z9%yk;F3Mut5t`d~m7MzZs8tU!KT8b|Q8)Sqwc;oV;F14*;$GE__h1o?FE1;3LP@~f zvAdMsx(6kqy2%lWkMvR9_$SmV2M^LbJiNGN2Woi9efdlcuWzFijgo*=`?KYk=aGs1 z7(=P@ENPy&z1&?Mp(QfV6qaohqa7f2qGcC$|n>vhtJ}w_c%? z!!IC=VEeKJEbMNldA!)z__ckva7P_)W?O?9t^UJ9+{sav0LDQ$1Ve(~0UnhSAbZv? zfR`G6cxDQ|8wv!3;s4TF3h?(OV^u(y5h}=@mGvcKRX~^#D#)Id^(A9fK$sCK$exw; zC1X`Um=P+-o|W|_BTvAJUrOi-@VDn*P%*47Rmg7G?i$Yo4Ju0jqq7#`<@D@@=j5zu zL`9$2%y(*haY?$IuKHTY1tQXIM_kcaY`3To!}bXq6X%@=pYcp&Um=t)pu*_(r6eGQ zFv&J;LaBy7>N&i6&Y~2Jk^pbUVCp6Q)ix( zoUFQtg_N7z!GsM%NdV(+E$+WobrEcQ1KtbDTZ`D-wXwO|f^t#V6TEwtfxEq&s1zLY z269Vtb9WjZSoz!QUGP8$Y9*s_uIBLDyZ literal 0 HcmV?d00001 diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ca07804eb9f59d640513fd7b65d67c2b483d5974 GIT binary patch literal 15086 zcmeHNF>hQ&5ME;kW6UKd5ej5L76K)KNSufiDr6%N1(CoFhK_^+Q4kuaLXrCcgqR)> zbfF3b)H(V{@DuzGk{^KdnD5(GxA4s9zwy782ZNd7RZOYv@)|!u`cx~z}=TjSl=I2W*&Fw3xx&4h4NET_M z@1MTZx{Z3x`pxYhtOw+jxVBBb-h;9iZc$&7u%+*K$$cyoJK&4w%%+rWDR@3b z{v`HL@;1)x{Sx<5KQLQt36bL>{THSGZV9JvKVk@l_8CbYh#kTG@}1QN#j`E-Yd>-j ziJXVV&3&hFbiLHCZD)CC=UeN$>LvVAzr_{~Rcoz;U+TXne&#ohHXA1g?Ogx*+4A9U zl{{dYZGro-b4r1dS?18+0$vYq4|HuKH^mrFhZ^vz7lgDf>u8K`~O-4Ba zQ_5B5oH6%PTAie{JjOM_5&a?7L}KnsY}`|fq+FB|y{8bu4U@IgvZ;+!(+;iev5Ydx~Q9 zUc1)b;t1FOi~;k~C-3|nJ+(2tOSkcCyVl% zxgX{FYjm}IYVAEP;4X8q=k-VRwR~#rJs#jL^AP?H=Hb-xskQgwf_u%r#s0)Q9Pi-B z<9d82UZa%YBU=IXMdM93x`Y%tL7`m{(%sz4*P? z4(|a=zR~`3FRr)ugp8x_8lo7UonG&VzOu3UJ$^ZtkLh>);_PwxJrD^?_D*f%%_=;C1_exH4fHDbr0V`&bkm-aMrqWyDq zoda!;{f0Y@NYT#&U}(%-Os%u_+E??@KDw^$d6$GWq4kkE$AGn@?RBhNy^Fub(RGcZ z^?mqzzFsUHx3=zKdK@D7p05`xg6Y|Km>vfYN9#RbFP8SLt$UarhX}ss>&1#-dNv-W z$HBwVde7I3rG0Db9;U}3g75iyu_BnBjfd%R@Nl%=^YvnB-`cu|>2ZkQd%j+*2&QM_ zVR{@q9If|!y;$0}t6p>d@i=thv(NL@{1<)x@i>rUmt4g8q4xZv?e+OMQfHp+d4_sV zTKldXu}@=B2Z^&Uzej4DXdU11y3Wr!R-d}&V{s54--aXSW^H4?Clj|T7Hw+fA}~D; zi-@Jy$Wgt&Bc?aTT0Nf?$PM-;?g?r!O7_SLu$h0vEcrzHfj#+Z@po_Jj##C4-6(h3 z;ZE-;#()?#_lI%^8vZtndBD8t{lJ{{mB0jcP2R!%e{g^PaDTse$iE-8-@pcgo7|`K zNUnXH7bE_)_Bl*h_csr|KTf}&hWD|>BcJHrtF^cB+I?%j#ml$6xxZiIEQ4a>ce7&~V;@j|b@w@dP=A_N_ zH1>6H@p%4&j;)mLLY_!zA96LN3FLB0V@UZACQ39P{rMRfqf6y$g0|#wA5!wlJTveA E0-!22P5=M^ literal 0 HcmV?d00001 diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..43c9633 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/public/manifest.webmanifest b/frontend/public/manifest.webmanifest new file mode 100644 index 0000000..39618e4 --- /dev/null +++ b/frontend/public/manifest.webmanifest @@ -0,0 +1,51 @@ +{ + "name": "Velxio — Arduino Emulator", + "short_name": "Velxio", + "description": "Free local Arduino emulator with real AVR8 CPU emulation, 48+ electronic components, Monaco Editor, and Serial Monitor. No cloud, no latency.", + "start_url": "/editor", + "scope": "/", + "display": "standalone", + "orientation": "landscape", + "background_color": "#0d0d0f", + "theme_color": "#007acc", + "categories": ["developer", "education", "productivity"], + "lang": "en", + "icons": [ + { + "src": "/favicon-16x16.png", + "sizes": "16x16", + "type": "image/png" + }, + { + "src": "/favicon-32x32.png", + "sizes": "32x32", + "type": "image/png" + }, + { + "src": "/android-chrome-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/android-chrome-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/favicon.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any maskable" + } + ], + "screenshots": [ + { + "src": "/og-image.svg", + "sizes": "1200x630", + "type": "image/svg+xml", + "label": "Velxio Arduino Emulator" + } + ] +} diff --git a/frontend/public/og-image.svg b/frontend/public/og-image.svg new file mode 100644 index 0000000..e4a8f89 --- /dev/null +++ b/frontend/public/og-image.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OPEN SOURCE · FREE · LOCAL + + + + + + + + + + + + + + + + + VELXIO + + + + Arduino Emulator — Real AVR8 · 48+ Components · Monaco Editor + + + + + + + arduino-cli + + + avr8js + + + rp2040js + + + TypeScript + + + FastAPI + + + React + + + + + + + + github.com/davidmonterocrespo24/velxio + + diff --git a/frontend/public/robots.txt b/frontend/public/robots.txt new file mode 100644 index 0000000..25208a3 --- /dev/null +++ b/frontend/public/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +Allow: / + +# Sitemap +Sitemap: https://velxio.dev/sitemap.xml diff --git a/frontend/public/sitemap.xml b/frontend/public/sitemap.xml new file mode 100644 index 0000000..964b4eb --- /dev/null +++ b/frontend/public/sitemap.xml @@ -0,0 +1,40 @@ + + + + + https://velxio.dev/ + 2026-03-06 + weekly + 1.0 + + + + https://velxio.dev/editor + 2026-03-06 + weekly + 0.9 + + + + https://velxio.dev/examples + 2026-03-06 + monthly + 0.7 + + + + https://velxio.dev/login + 2026-03-06 + yearly + 0.3 + + + + https://velxio.dev/register + 2026-03-06 + yearly + 0.3 + + + diff --git a/scripts/generate-favicons.mjs b/scripts/generate-favicons.mjs new file mode 100644 index 0000000..491e17c --- /dev/null +++ b/scripts/generate-favicons.mjs @@ -0,0 +1,77 @@ +/** + * Favicon generator — converts favicon.svg into all required sizes. + * Run from project root: node scripts/generate-favicons.mjs + * + * Generates: + * frontend/public/favicon-16x16.png + * frontend/public/favicon-32x32.png + * frontend/public/favicon-48x48.png + * frontend/public/apple-touch-icon.png (180x180) + * frontend/public/android-chrome-192.png + * frontend/public/android-chrome-512.png + * frontend/public/favicon.ico (16+32+48 multi-size) + */ + +import { readFileSync, writeFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const ROOT = join(__dirname, '..'); +const PUBLIC = join(ROOT, 'frontend', 'public'); + +// ── install deps on the fly if missing ───────────────────────────── +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); + +async function ensureDep(pkg) { + try { return await import(pkg); } catch {} + console.log(`Installing ${pkg}…`); + const { execSync } = await import('child_process'); + execSync(`npm install --no-save ${pkg}`, { stdio: 'inherit', cwd: ROOT }); + return await import(pkg); +} + +const { Resvg } = await ensureDep('@resvg/resvg-js'); +const pngToIcoMod = await ensureDep('png-to-ico'); +const pngToIco = pngToIcoMod.default ?? pngToIcoMod; + +// ── render helper ─────────────────────────────────────────────────── +const svgSrc = readFileSync(join(PUBLIC, 'favicon.svg')); + +function renderPng(size) { + const resvg = new Resvg(svgSrc, { + fitTo: { mode: 'width', value: size }, + font: { loadSystemFonts: false }, + }); + return resvg.render().asPng(); +} + +// ── generate PNGs ─────────────────────────────────────────────────── +const sizes = [ + { name: 'favicon-16x16.png', size: 16 }, + { name: 'favicon-32x32.png', size: 32 }, + { name: 'favicon-48x48.png', size: 48 }, + { name: 'apple-touch-icon.png', size: 180 }, + { name: 'android-chrome-192.png', size: 192 }, + { name: 'android-chrome-512.png', size: 512 }, +]; + +const pngBuffers = {}; +for (const { name, size } of sizes) { + const buf = renderPng(size); + writeFileSync(join(PUBLIC, name), buf); + pngBuffers[size] = buf; + console.log(`✓ ${name} (${size}x${size})`); +} + +// ── generate favicon.ico (16 + 32 + 48) ──────────────────────────── +const icoBuffer = await pngToIco([ + pngBuffers[16], + pngBuffers[32], + pngBuffers[48], +]); +writeFileSync(join(PUBLIC, 'favicon.ico'), icoBuffer); +console.log('✓ favicon.ico (16+32+48)'); + +console.log('\nAll favicon assets generated in frontend/public/');